1 // Copyright 2013 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/basictypes.h" 6 #include "base/bind.h" 7 #include "base/file_util.h" 8 #include "base/memory/scoped_ptr.h" 9 #include "base/path_service.h" 10 #include "base/stl_util.h" 11 #include "base/strings/string_util.h" 12 #include "base/strings/utf_string_conversions.h" 13 #include "base/time/time.h" 14 #include "content/browser/frame_host/cross_site_transferring_request.h" 15 #include "content/browser/frame_host/navigation_controller_impl.h" 16 #include "content/browser/frame_host/navigation_entry_impl.h" 17 #include "content/browser/frame_host/navigation_entry_screenshot_manager.h" 18 #include "content/browser/frame_host/navigator.h" 19 #include "content/browser/site_instance_impl.h" 20 #include "content/browser/web_contents/web_contents_impl.h" 21 #include "content/common/frame_messages.h" 22 #include "content/common/view_messages.h" 23 #include "content/public/browser/navigation_details.h" 24 #include "content/public/browser/notification_registrar.h" 25 #include "content/public/browser/notification_types.h" 26 #include "content/public/browser/render_view_host.h" 27 #include "content/public/browser/web_contents_delegate.h" 28 #include "content/public/browser/web_contents_observer.h" 29 #include "content/public/common/page_state.h" 30 #include "content/public/common/url_constants.h" 31 #include "content/public/test/mock_render_process_host.h" 32 #include "content/public/test/test_notification_tracker.h" 33 #include "content/public/test/test_utils.h" 34 #include "content/test/test_render_view_host.h" 35 #include "content/test/test_web_contents.h" 36 #include "net/base/net_util.h" 37 #include "skia/ext/platform_canvas.h" 38 #include "testing/gtest/include/gtest/gtest.h" 39 40 using base::Time; 41 42 namespace { 43 44 // Creates an image with a 1x1 SkBitmap of the specified |color|. 45 gfx::Image CreateImage(SkColor color) { 46 SkBitmap bitmap; 47 bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1, 1); 48 bitmap.allocPixels(); 49 bitmap.eraseColor(color); 50 return gfx::Image::CreateFrom1xBitmap(bitmap); 51 } 52 53 // Returns true if images |a| and |b| have the same pixel data. 54 bool DoImagesMatch(const gfx::Image& a, const gfx::Image& b) { 55 // Assume that if the 1x bitmaps match, the images match. 56 SkBitmap a_bitmap = a.AsBitmap(); 57 SkBitmap b_bitmap = b.AsBitmap(); 58 59 if (a_bitmap.width() != b_bitmap.width() || 60 a_bitmap.height() != b_bitmap.height()) { 61 return false; 62 } 63 SkAutoLockPixels a_bitmap_lock(a_bitmap); 64 SkAutoLockPixels b_bitmap_lock(b_bitmap); 65 return memcmp(a_bitmap.getPixels(), 66 b_bitmap.getPixels(), 67 a_bitmap.getSize()) == 0; 68 } 69 70 class MockScreenshotManager : public content::NavigationEntryScreenshotManager { 71 public: 72 explicit MockScreenshotManager(content::NavigationControllerImpl* owner) 73 : content::NavigationEntryScreenshotManager(owner), 74 encoding_screenshot_in_progress_(false) { 75 } 76 77 virtual ~MockScreenshotManager() { 78 } 79 80 void TakeScreenshotFor(content::NavigationEntryImpl* entry) { 81 SkBitmap bitmap; 82 bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1, 1); 83 bitmap.allocPixels(); 84 bitmap.eraseARGB(0, 0, 0, 0); 85 encoding_screenshot_in_progress_ = true; 86 OnScreenshotTaken(entry->GetUniqueID(), true, bitmap); 87 WaitUntilScreenshotIsReady(); 88 } 89 90 int GetScreenshotCount() { 91 return content::NavigationEntryScreenshotManager::GetScreenshotCount(); 92 } 93 94 void WaitUntilScreenshotIsReady() { 95 if (!encoding_screenshot_in_progress_) 96 return; 97 message_loop_runner_ = new content::MessageLoopRunner; 98 message_loop_runner_->Run(); 99 } 100 101 private: 102 // Overridden from content::NavigationEntryScreenshotManager: 103 virtual void TakeScreenshotImpl( 104 content::RenderViewHost* host, 105 content::NavigationEntryImpl* entry) OVERRIDE { 106 } 107 108 virtual void OnScreenshotSet(content::NavigationEntryImpl* entry) OVERRIDE { 109 encoding_screenshot_in_progress_ = false; 110 NavigationEntryScreenshotManager::OnScreenshotSet(entry); 111 if (message_loop_runner_.get()) 112 message_loop_runner_->Quit(); 113 } 114 115 scoped_refptr<content::MessageLoopRunner> message_loop_runner_; 116 bool encoding_screenshot_in_progress_; 117 118 DISALLOW_COPY_AND_ASSIGN(MockScreenshotManager); 119 }; 120 121 } // namespace 122 123 namespace content { 124 125 // TimeSmoother tests ---------------------------------------------------------- 126 127 // With no duplicates, GetSmoothedTime should be the identity 128 // function. 129 TEST(TimeSmoother, Basic) { 130 NavigationControllerImpl::TimeSmoother smoother; 131 for (int64 i = 1; i < 1000; ++i) { 132 base::Time t = base::Time::FromInternalValue(i); 133 EXPECT_EQ(t, smoother.GetSmoothedTime(t)); 134 } 135 } 136 137 // With a single duplicate and timestamps thereafter increasing by one 138 // microsecond, the smoothed time should always be one behind. 139 TEST(TimeSmoother, SingleDuplicate) { 140 NavigationControllerImpl::TimeSmoother smoother; 141 base::Time t = base::Time::FromInternalValue(1); 142 EXPECT_EQ(t, smoother.GetSmoothedTime(t)); 143 for (int64 i = 1; i < 1000; ++i) { 144 base::Time expected_t = base::Time::FromInternalValue(i + 1); 145 t = base::Time::FromInternalValue(i); 146 EXPECT_EQ(expected_t, smoother.GetSmoothedTime(t)); 147 } 148 } 149 150 // With k duplicates and timestamps thereafter increasing by one 151 // microsecond, the smoothed time should always be k behind. 152 TEST(TimeSmoother, ManyDuplicates) { 153 const int64 kNumDuplicates = 100; 154 NavigationControllerImpl::TimeSmoother smoother; 155 base::Time t = base::Time::FromInternalValue(1); 156 for (int64 i = 0; i < kNumDuplicates; ++i) { 157 base::Time expected_t = base::Time::FromInternalValue(i + 1); 158 EXPECT_EQ(expected_t, smoother.GetSmoothedTime(t)); 159 } 160 for (int64 i = 1; i < 1000; ++i) { 161 base::Time expected_t = 162 base::Time::FromInternalValue(i + kNumDuplicates); 163 t = base::Time::FromInternalValue(i); 164 EXPECT_EQ(expected_t, smoother.GetSmoothedTime(t)); 165 } 166 } 167 168 // If the clock jumps far back enough after a run of duplicates, it 169 // should immediately jump to that value. 170 TEST(TimeSmoother, ClockBackwardsJump) { 171 const int64 kNumDuplicates = 100; 172 NavigationControllerImpl::TimeSmoother smoother; 173 base::Time t = base::Time::FromInternalValue(1000); 174 for (int64 i = 0; i < kNumDuplicates; ++i) { 175 base::Time expected_t = base::Time::FromInternalValue(i + 1000); 176 EXPECT_EQ(expected_t, smoother.GetSmoothedTime(t)); 177 } 178 t = base::Time::FromInternalValue(500); 179 EXPECT_EQ(t, smoother.GetSmoothedTime(t)); 180 } 181 182 // NavigationControllerTest ---------------------------------------------------- 183 184 class NavigationControllerTest 185 : public RenderViewHostImplTestHarness, 186 public WebContentsObserver { 187 public: 188 NavigationControllerTest() : navigation_entry_committed_counter_(0) { 189 } 190 191 virtual void SetUp() OVERRIDE { 192 RenderViewHostImplTestHarness::SetUp(); 193 WebContents* web_contents = RenderViewHostImplTestHarness::web_contents(); 194 ASSERT_TRUE(web_contents); // The WebContents should be created by now. 195 WebContentsObserver::Observe(web_contents); 196 } 197 198 // WebContentsObserver: 199 virtual void DidStartNavigationToPendingEntry( 200 const GURL& url, 201 NavigationController::ReloadType reload_type) OVERRIDE { 202 navigated_url_ = url; 203 } 204 205 virtual void NavigationEntryCommitted( 206 const LoadCommittedDetails& load_details) OVERRIDE { 207 navigation_entry_committed_counter_++; 208 } 209 210 const GURL& navigated_url() const { 211 return navigated_url_; 212 } 213 214 NavigationControllerImpl& controller_impl() { 215 return static_cast<NavigationControllerImpl&>(controller()); 216 } 217 218 protected: 219 GURL navigated_url_; 220 size_t navigation_entry_committed_counter_; 221 }; 222 223 void RegisterForAllNavNotifications(TestNotificationTracker* tracker, 224 NavigationController* controller) { 225 tracker->ListenFor(NOTIFICATION_NAV_LIST_PRUNED, 226 Source<NavigationController>(controller)); 227 tracker->ListenFor(NOTIFICATION_NAV_ENTRY_CHANGED, 228 Source<NavigationController>(controller)); 229 } 230 231 SiteInstance* GetSiteInstanceFromEntry(NavigationEntry* entry) { 232 return NavigationEntryImpl::FromNavigationEntry(entry)->site_instance(); 233 } 234 235 class TestWebContentsDelegate : public WebContentsDelegate { 236 public: 237 explicit TestWebContentsDelegate() : 238 navigation_state_change_count_(0), 239 repost_form_warning_count_(0) {} 240 241 int navigation_state_change_count() { 242 return navigation_state_change_count_; 243 } 244 245 int repost_form_warning_count() { 246 return repost_form_warning_count_; 247 } 248 249 // Keep track of whether the tab has notified us of a navigation state change. 250 virtual void NavigationStateChanged(const WebContents* source, 251 unsigned changed_flags) OVERRIDE { 252 navigation_state_change_count_++; 253 } 254 255 virtual void ShowRepostFormWarningDialog(WebContents* source) OVERRIDE { 256 repost_form_warning_count_++; 257 } 258 259 private: 260 // The number of times NavigationStateChanged has been called. 261 int navigation_state_change_count_; 262 263 // The number of times ShowRepostFormWarningDialog() was called. 264 int repost_form_warning_count_; 265 }; 266 267 // ----------------------------------------------------------------------------- 268 269 TEST_F(NavigationControllerTest, Defaults) { 270 NavigationControllerImpl& controller = controller_impl(); 271 272 EXPECT_FALSE(controller.GetPendingEntry()); 273 EXPECT_FALSE(controller.GetVisibleEntry()); 274 EXPECT_FALSE(controller.GetLastCommittedEntry()); 275 EXPECT_EQ(controller.GetPendingEntryIndex(), -1); 276 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), -1); 277 EXPECT_EQ(controller.GetEntryCount(), 0); 278 EXPECT_FALSE(controller.CanGoBack()); 279 EXPECT_FALSE(controller.CanGoForward()); 280 } 281 282 TEST_F(NavigationControllerTest, GoToOffset) { 283 NavigationControllerImpl& controller = controller_impl(); 284 TestNotificationTracker notifications; 285 RegisterForAllNavNotifications(¬ifications, &controller); 286 287 const int kNumUrls = 5; 288 std::vector<GURL> urls(kNumUrls); 289 for (int i = 0; i < kNumUrls; ++i) { 290 urls[i] = GURL(base::StringPrintf("http://www.a.com/%d", i)); 291 } 292 293 main_test_rfh()->SendNavigate(0, urls[0]); 294 EXPECT_EQ(1U, navigation_entry_committed_counter_); 295 navigation_entry_committed_counter_ = 0; 296 EXPECT_EQ(urls[0], controller.GetVisibleEntry()->GetVirtualURL()); 297 EXPECT_FALSE(controller.CanGoBack()); 298 EXPECT_FALSE(controller.CanGoForward()); 299 EXPECT_FALSE(controller.CanGoToOffset(1)); 300 301 for (int i = 1; i <= 4; ++i) { 302 main_test_rfh()->SendNavigate(i, urls[i]); 303 EXPECT_EQ(1U, navigation_entry_committed_counter_); 304 navigation_entry_committed_counter_ = 0; 305 EXPECT_EQ(urls[i], controller.GetVisibleEntry()->GetVirtualURL()); 306 EXPECT_TRUE(controller.CanGoToOffset(-i)); 307 EXPECT_FALSE(controller.CanGoToOffset(-(i + 1))); 308 EXPECT_FALSE(controller.CanGoToOffset(1)); 309 } 310 311 // We have loaded 5 pages, and are currently at the last-loaded page. 312 int url_index = 4; 313 314 enum Tests { 315 GO_TO_MIDDLE_PAGE = -2, 316 GO_FORWARDS = 1, 317 GO_BACKWARDS = -1, 318 GO_TO_BEGINNING = -2, 319 GO_TO_END = 4, 320 NUM_TESTS = 5, 321 }; 322 323 const int test_offsets[NUM_TESTS] = { 324 GO_TO_MIDDLE_PAGE, 325 GO_FORWARDS, 326 GO_BACKWARDS, 327 GO_TO_BEGINNING, 328 GO_TO_END 329 }; 330 331 for (int test = 0; test < NUM_TESTS; ++test) { 332 int offset = test_offsets[test]; 333 controller.GoToOffset(offset); 334 url_index += offset; 335 // Check that the GoToOffset will land on the expected page. 336 EXPECT_EQ(urls[url_index], controller.GetPendingEntry()->GetVirtualURL()); 337 main_test_rfh()->SendNavigate(url_index, urls[url_index]); 338 EXPECT_EQ(1U, navigation_entry_committed_counter_); 339 navigation_entry_committed_counter_ = 0; 340 // Check that we can go to any valid offset into the history. 341 for (size_t j = 0; j < urls.size(); ++j) 342 EXPECT_TRUE(controller.CanGoToOffset(j - url_index)); 343 // Check that we can't go beyond the beginning or end of the history. 344 EXPECT_FALSE(controller.CanGoToOffset(-(url_index + 1))); 345 EXPECT_FALSE(controller.CanGoToOffset(urls.size() - url_index)); 346 } 347 } 348 349 TEST_F(NavigationControllerTest, LoadURL) { 350 NavigationControllerImpl& controller = controller_impl(); 351 TestNotificationTracker notifications; 352 RegisterForAllNavNotifications(¬ifications, &controller); 353 354 const GURL url1("http://foo1"); 355 const GURL url2("http://foo2"); 356 357 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 358 // Creating a pending notification should not have issued any of the 359 // notifications we're listening for. 360 EXPECT_EQ(0U, notifications.size()); 361 362 // The load should now be pending. 363 EXPECT_EQ(controller.GetEntryCount(), 0); 364 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), -1); 365 EXPECT_EQ(controller.GetPendingEntryIndex(), -1); 366 EXPECT_FALSE(controller.GetLastCommittedEntry()); 367 ASSERT_TRUE(controller.GetPendingEntry()); 368 EXPECT_EQ(controller.GetPendingEntry(), controller.GetVisibleEntry()); 369 EXPECT_FALSE(controller.CanGoBack()); 370 EXPECT_FALSE(controller.CanGoForward()); 371 EXPECT_EQ(contents()->GetMaxPageID(), -1); 372 373 // Neither the timestamp nor the status code should have been set yet. 374 EXPECT_TRUE(controller.GetPendingEntry()->GetTimestamp().is_null()); 375 EXPECT_EQ(0, controller.GetPendingEntry()->GetHttpStatusCode()); 376 377 // We should have gotten no notifications from the preceeding checks. 378 EXPECT_EQ(0U, notifications.size()); 379 380 main_test_rfh()->SendNavigate(0, url1); 381 EXPECT_EQ(1U, navigation_entry_committed_counter_); 382 navigation_entry_committed_counter_ = 0; 383 384 // The load should now be committed. 385 EXPECT_EQ(controller.GetEntryCount(), 1); 386 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0); 387 EXPECT_EQ(controller.GetPendingEntryIndex(), -1); 388 EXPECT_TRUE(controller.GetLastCommittedEntry()); 389 EXPECT_FALSE(controller.GetPendingEntry()); 390 ASSERT_TRUE(controller.GetVisibleEntry()); 391 EXPECT_FALSE(controller.CanGoBack()); 392 EXPECT_FALSE(controller.CanGoForward()); 393 EXPECT_EQ(contents()->GetMaxPageID(), 0); 394 EXPECT_EQ(0, NavigationEntryImpl::FromNavigationEntry( 395 controller.GetLastCommittedEntry())->bindings()); 396 397 // The timestamp should have been set. 398 EXPECT_FALSE(controller.GetVisibleEntry()->GetTimestamp().is_null()); 399 400 // Load another... 401 controller.LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 402 403 // The load should now be pending. 404 EXPECT_EQ(controller.GetEntryCount(), 1); 405 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0); 406 EXPECT_EQ(controller.GetPendingEntryIndex(), -1); 407 EXPECT_TRUE(controller.GetLastCommittedEntry()); 408 ASSERT_TRUE(controller.GetPendingEntry()); 409 EXPECT_EQ(controller.GetPendingEntry(), controller.GetVisibleEntry()); 410 // TODO(darin): maybe this should really be true? 411 EXPECT_FALSE(controller.CanGoBack()); 412 EXPECT_FALSE(controller.CanGoForward()); 413 EXPECT_EQ(contents()->GetMaxPageID(), 0); 414 415 EXPECT_TRUE(controller.GetPendingEntry()->GetTimestamp().is_null()); 416 417 // Simulate the beforeunload ack for the cross-site transition, and then the 418 // commit. 419 test_rvh()->SendBeforeUnloadACK(true); 420 static_cast<TestRenderViewHost*>( 421 contents()->GetPendingRenderViewHost())->SendNavigate(1, url2); 422 EXPECT_EQ(1U, navigation_entry_committed_counter_); 423 navigation_entry_committed_counter_ = 0; 424 425 // The load should now be committed. 426 EXPECT_EQ(controller.GetEntryCount(), 2); 427 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1); 428 EXPECT_EQ(controller.GetPendingEntryIndex(), -1); 429 EXPECT_TRUE(controller.GetLastCommittedEntry()); 430 EXPECT_FALSE(controller.GetPendingEntry()); 431 ASSERT_TRUE(controller.GetVisibleEntry()); 432 EXPECT_TRUE(controller.CanGoBack()); 433 EXPECT_FALSE(controller.CanGoForward()); 434 EXPECT_EQ(contents()->GetMaxPageID(), 1); 435 436 EXPECT_FALSE(controller.GetVisibleEntry()->GetTimestamp().is_null()); 437 } 438 439 namespace { 440 441 base::Time GetFixedTime(base::Time time) { 442 return time; 443 } 444 445 } // namespace 446 447 TEST_F(NavigationControllerTest, LoadURLSameTime) { 448 NavigationControllerImpl& controller = controller_impl(); 449 TestNotificationTracker notifications; 450 RegisterForAllNavNotifications(¬ifications, &controller); 451 452 // Set the clock to always return a timestamp of 1. 453 controller.SetGetTimestampCallbackForTest( 454 base::Bind(&GetFixedTime, base::Time::FromInternalValue(1))); 455 456 const GURL url1("http://foo1"); 457 const GURL url2("http://foo2"); 458 459 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 460 461 main_test_rfh()->SendNavigate(0, url1); 462 EXPECT_EQ(1U, navigation_entry_committed_counter_); 463 navigation_entry_committed_counter_ = 0; 464 465 // Load another... 466 controller.LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 467 468 // Simulate the beforeunload ack for the cross-site transition, and then the 469 // commit. 470 test_rvh()->SendBeforeUnloadACK(true); 471 main_test_rfh()->SendNavigate(1, url2); 472 EXPECT_EQ(1U, navigation_entry_committed_counter_); 473 navigation_entry_committed_counter_ = 0; 474 475 // The two loads should now be committed. 476 ASSERT_EQ(controller.GetEntryCount(), 2); 477 478 // Timestamps should be distinct despite the clock returning the 479 // same value. 480 EXPECT_EQ(1u, 481 controller.GetEntryAtIndex(0)->GetTimestamp().ToInternalValue()); 482 EXPECT_EQ(2u, 483 controller.GetEntryAtIndex(1)->GetTimestamp().ToInternalValue()); 484 } 485 486 void CheckNavigationEntryMatchLoadParams( 487 NavigationController::LoadURLParams& load_params, 488 NavigationEntryImpl* entry) { 489 EXPECT_EQ(load_params.url, entry->GetURL()); 490 EXPECT_EQ(load_params.referrer.url, entry->GetReferrer().url); 491 EXPECT_EQ(load_params.referrer.policy, entry->GetReferrer().policy); 492 EXPECT_EQ(load_params.transition_type, entry->GetTransitionType()); 493 EXPECT_EQ(load_params.extra_headers, entry->extra_headers()); 494 495 EXPECT_EQ(load_params.is_renderer_initiated, entry->is_renderer_initiated()); 496 EXPECT_EQ(load_params.base_url_for_data_url, entry->GetBaseURLForDataURL()); 497 if (!load_params.virtual_url_for_data_url.is_empty()) { 498 EXPECT_EQ(load_params.virtual_url_for_data_url, entry->GetVirtualURL()); 499 } 500 if (NavigationController::UA_OVERRIDE_INHERIT != 501 load_params.override_user_agent) { 502 bool should_override = (NavigationController::UA_OVERRIDE_TRUE == 503 load_params.override_user_agent); 504 EXPECT_EQ(should_override, entry->GetIsOverridingUserAgent()); 505 } 506 EXPECT_EQ(load_params.browser_initiated_post_data, 507 entry->GetBrowserInitiatedPostData()); 508 EXPECT_EQ(load_params.transferred_global_request_id, 509 entry->transferred_global_request_id()); 510 } 511 512 TEST_F(NavigationControllerTest, LoadURLWithParams) { 513 NavigationControllerImpl& controller = controller_impl(); 514 515 NavigationController::LoadURLParams load_params(GURL("http://foo")); 516 load_params.referrer = 517 Referrer(GURL("http://referrer"), blink::WebReferrerPolicyDefault); 518 load_params.transition_type = PAGE_TRANSITION_GENERATED; 519 load_params.extra_headers = "content-type: text/plain"; 520 load_params.load_type = NavigationController::LOAD_TYPE_DEFAULT; 521 load_params.is_renderer_initiated = true; 522 load_params.override_user_agent = NavigationController::UA_OVERRIDE_TRUE; 523 load_params.transferred_global_request_id = GlobalRequestID(2, 3); 524 525 controller.LoadURLWithParams(load_params); 526 NavigationEntryImpl* entry = 527 NavigationEntryImpl::FromNavigationEntry( 528 controller.GetPendingEntry()); 529 530 // The timestamp should not have been set yet. 531 ASSERT_TRUE(entry); 532 EXPECT_TRUE(entry->GetTimestamp().is_null()); 533 534 CheckNavigationEntryMatchLoadParams(load_params, entry); 535 } 536 537 TEST_F(NavigationControllerTest, LoadURLWithExtraParams_Data) { 538 NavigationControllerImpl& controller = controller_impl(); 539 540 NavigationController::LoadURLParams load_params( 541 GURL("data:text/html,dataurl")); 542 load_params.load_type = NavigationController::LOAD_TYPE_DATA; 543 load_params.base_url_for_data_url = GURL("http://foo"); 544 load_params.virtual_url_for_data_url = GURL(url::kAboutBlankURL); 545 load_params.override_user_agent = NavigationController::UA_OVERRIDE_FALSE; 546 547 controller.LoadURLWithParams(load_params); 548 NavigationEntryImpl* entry = 549 NavigationEntryImpl::FromNavigationEntry( 550 controller.GetPendingEntry()); 551 552 CheckNavigationEntryMatchLoadParams(load_params, entry); 553 } 554 555 TEST_F(NavigationControllerTest, LoadURLWithExtraParams_HttpPost) { 556 NavigationControllerImpl& controller = controller_impl(); 557 558 NavigationController::LoadURLParams load_params(GURL("https://posturl")); 559 load_params.transition_type = PAGE_TRANSITION_TYPED; 560 load_params.load_type = 561 NavigationController::LOAD_TYPE_BROWSER_INITIATED_HTTP_POST; 562 load_params.override_user_agent = NavigationController::UA_OVERRIDE_TRUE; 563 564 565 const unsigned char* raw_data = 566 reinterpret_cast<const unsigned char*>("d\n\0a2"); 567 const int length = 5; 568 std::vector<unsigned char> post_data_vector(raw_data, raw_data+length); 569 scoped_refptr<base::RefCountedBytes> data = 570 base::RefCountedBytes::TakeVector(&post_data_vector); 571 load_params.browser_initiated_post_data = data.get(); 572 573 controller.LoadURLWithParams(load_params); 574 NavigationEntryImpl* entry = 575 NavigationEntryImpl::FromNavigationEntry( 576 controller.GetPendingEntry()); 577 578 CheckNavigationEntryMatchLoadParams(load_params, entry); 579 } 580 581 // Tests what happens when the same page is loaded again. Should not create a 582 // new session history entry. This is what happens when you press enter in the 583 // URL bar to reload: a pending entry is created and then it is discarded when 584 // the load commits (because WebCore didn't actually make a new entry). 585 TEST_F(NavigationControllerTest, LoadURL_SamePage) { 586 NavigationControllerImpl& controller = controller_impl(); 587 TestNotificationTracker notifications; 588 RegisterForAllNavNotifications(¬ifications, &controller); 589 590 const GURL url1("http://foo1"); 591 592 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 593 EXPECT_EQ(0U, notifications.size()); 594 main_test_rfh()->SendNavigate(0, url1); 595 EXPECT_EQ(1U, navigation_entry_committed_counter_); 596 navigation_entry_committed_counter_ = 0; 597 598 ASSERT_TRUE(controller.GetVisibleEntry()); 599 const base::Time timestamp = controller.GetVisibleEntry()->GetTimestamp(); 600 EXPECT_FALSE(timestamp.is_null()); 601 602 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 603 EXPECT_EQ(0U, notifications.size()); 604 main_test_rfh()->SendNavigate(0, url1); 605 EXPECT_EQ(1U, navigation_entry_committed_counter_); 606 navigation_entry_committed_counter_ = 0; 607 608 // We should not have produced a new session history entry. 609 EXPECT_EQ(controller.GetEntryCount(), 1); 610 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0); 611 EXPECT_EQ(controller.GetPendingEntryIndex(), -1); 612 EXPECT_TRUE(controller.GetLastCommittedEntry()); 613 EXPECT_FALSE(controller.GetPendingEntry()); 614 ASSERT_TRUE(controller.GetVisibleEntry()); 615 EXPECT_FALSE(controller.CanGoBack()); 616 EXPECT_FALSE(controller.CanGoForward()); 617 618 // The timestamp should have been updated. 619 // 620 // TODO(akalin): Change this EXPECT_GE (and other similar ones) to 621 // EXPECT_GT once we guarantee that timestamps are unique. 622 EXPECT_GE(controller.GetVisibleEntry()->GetTimestamp(), timestamp); 623 } 624 625 // Load the same page twice, once as a GET and once as a POST. 626 // We should update the post state on the NavigationEntry. 627 TEST_F(NavigationControllerTest, LoadURL_SamePage_DifferentMethod) { 628 NavigationControllerImpl& controller = controller_impl(); 629 TestNotificationTracker notifications; 630 RegisterForAllNavNotifications(¬ifications, &controller); 631 632 const GURL url1("http://foo1"); 633 634 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 635 FrameHostMsg_DidCommitProvisionalLoad_Params params; 636 params.page_id = 0; 637 params.url = url1; 638 params.transition = PAGE_TRANSITION_TYPED; 639 params.is_post = true; 640 params.post_id = 123; 641 params.page_state = PageState::CreateForTesting(url1, false, 0, 0); 642 main_test_rfh()->SendNavigateWithParams(¶ms); 643 644 // The post data should be visible. 645 NavigationEntry* entry = controller.GetVisibleEntry(); 646 ASSERT_TRUE(entry); 647 EXPECT_TRUE(entry->GetHasPostData()); 648 EXPECT_EQ(entry->GetPostID(), 123); 649 650 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 651 main_test_rfh()->SendNavigate(0, url1); 652 653 // We should not have produced a new session history entry. 654 ASSERT_EQ(controller.GetVisibleEntry(), entry); 655 656 // The post data should have been cleared due to the GET. 657 EXPECT_FALSE(entry->GetHasPostData()); 658 EXPECT_EQ(entry->GetPostID(), 0); 659 } 660 661 // Tests loading a URL but discarding it before the load commits. 662 TEST_F(NavigationControllerTest, LoadURL_Discarded) { 663 NavigationControllerImpl& controller = controller_impl(); 664 TestNotificationTracker notifications; 665 RegisterForAllNavNotifications(¬ifications, &controller); 666 667 const GURL url1("http://foo1"); 668 const GURL url2("http://foo2"); 669 670 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 671 EXPECT_EQ(0U, notifications.size()); 672 main_test_rfh()->SendNavigate(0, url1); 673 EXPECT_EQ(1U, navigation_entry_committed_counter_); 674 navigation_entry_committed_counter_ = 0; 675 676 ASSERT_TRUE(controller.GetVisibleEntry()); 677 const base::Time timestamp = controller.GetVisibleEntry()->GetTimestamp(); 678 EXPECT_FALSE(timestamp.is_null()); 679 680 controller.LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 681 controller.DiscardNonCommittedEntries(); 682 EXPECT_EQ(0U, notifications.size()); 683 684 // Should not have produced a new session history entry. 685 EXPECT_EQ(controller.GetEntryCount(), 1); 686 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0); 687 EXPECT_EQ(controller.GetPendingEntryIndex(), -1); 688 EXPECT_TRUE(controller.GetLastCommittedEntry()); 689 EXPECT_FALSE(controller.GetPendingEntry()); 690 ASSERT_TRUE(controller.GetVisibleEntry()); 691 EXPECT_FALSE(controller.CanGoBack()); 692 EXPECT_FALSE(controller.CanGoForward()); 693 694 // Timestamp should not have changed. 695 EXPECT_EQ(timestamp, controller.GetVisibleEntry()->GetTimestamp()); 696 } 697 698 // Tests navigations that come in unrequested. This happens when the user 699 // navigates from the web page, and here we test that there is no pending entry. 700 TEST_F(NavigationControllerTest, LoadURL_NoPending) { 701 NavigationControllerImpl& controller = controller_impl(); 702 TestNotificationTracker notifications; 703 RegisterForAllNavNotifications(¬ifications, &controller); 704 705 // First make an existing committed entry. 706 const GURL kExistingURL1("http://eh"); 707 controller.LoadURL( 708 kExistingURL1, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 709 main_test_rfh()->SendNavigate(0, kExistingURL1); 710 EXPECT_EQ(1U, navigation_entry_committed_counter_); 711 navigation_entry_committed_counter_ = 0; 712 713 // Do a new navigation without making a pending one. 714 const GURL kNewURL("http://see"); 715 main_test_rfh()->SendNavigate(99, kNewURL); 716 717 // There should no longer be any pending entry, and the third navigation we 718 // just made should be committed. 719 EXPECT_EQ(1U, navigation_entry_committed_counter_); 720 navigation_entry_committed_counter_ = 0; 721 EXPECT_EQ(-1, controller.GetPendingEntryIndex()); 722 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex()); 723 EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL()); 724 } 725 726 // Tests navigating to a new URL when there is a new pending navigation that is 727 // not the one that just loaded. This will happen if the user types in a URL to 728 // somewhere slow, and then navigates the current page before the typed URL 729 // commits. 730 TEST_F(NavigationControllerTest, LoadURL_NewPending) { 731 NavigationControllerImpl& controller = controller_impl(); 732 TestNotificationTracker notifications; 733 RegisterForAllNavNotifications(¬ifications, &controller); 734 735 // First make an existing committed entry. 736 const GURL kExistingURL1("http://eh"); 737 controller.LoadURL( 738 kExistingURL1, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 739 main_test_rfh()->SendNavigate(0, kExistingURL1); 740 EXPECT_EQ(1U, navigation_entry_committed_counter_); 741 navigation_entry_committed_counter_ = 0; 742 743 // Make a pending entry to somewhere new. 744 const GURL kExistingURL2("http://bee"); 745 controller.LoadURL( 746 kExistingURL2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 747 EXPECT_EQ(0U, notifications.size()); 748 749 // After the beforeunload but before it commits, do a new navigation. 750 test_rvh()->SendBeforeUnloadACK(true); 751 const GURL kNewURL("http://see"); 752 static_cast<TestRenderViewHost*>( 753 contents()->GetPendingRenderViewHost())->SendNavigate(3, kNewURL); 754 755 // There should no longer be any pending entry, and the third navigation we 756 // just made should be committed. 757 EXPECT_EQ(1U, navigation_entry_committed_counter_); 758 navigation_entry_committed_counter_ = 0; 759 EXPECT_EQ(-1, controller.GetPendingEntryIndex()); 760 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex()); 761 EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL()); 762 } 763 764 // Tests navigating to a new URL when there is a pending back/forward 765 // navigation. This will happen if the user hits back, but before that commits, 766 // they navigate somewhere new. 767 TEST_F(NavigationControllerTest, LoadURL_ExistingPending) { 768 NavigationControllerImpl& controller = controller_impl(); 769 TestNotificationTracker notifications; 770 RegisterForAllNavNotifications(¬ifications, &controller); 771 772 // First make some history. 773 const GURL kExistingURL1("http://foo/eh"); 774 controller.LoadURL( 775 kExistingURL1, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 776 main_test_rfh()->SendNavigate(0, kExistingURL1); 777 EXPECT_EQ(1U, navigation_entry_committed_counter_); 778 navigation_entry_committed_counter_ = 0; 779 780 const GURL kExistingURL2("http://foo/bee"); 781 controller.LoadURL( 782 kExistingURL2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 783 main_test_rfh()->SendNavigate(1, kExistingURL2); 784 EXPECT_EQ(1U, navigation_entry_committed_counter_); 785 navigation_entry_committed_counter_ = 0; 786 787 // Now make a pending back/forward navigation. The zeroth entry should be 788 // pending. 789 controller.GoBack(); 790 EXPECT_EQ(0U, notifications.size()); 791 EXPECT_EQ(0, controller.GetPendingEntryIndex()); 792 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex()); 793 794 // Before that commits, do a new navigation. 795 const GURL kNewURL("http://foo/see"); 796 LoadCommittedDetails details; 797 main_test_rfh()->SendNavigate(3, kNewURL); 798 799 // There should no longer be any pending entry, and the third navigation we 800 // just made should be committed. 801 EXPECT_EQ(1U, navigation_entry_committed_counter_); 802 navigation_entry_committed_counter_ = 0; 803 EXPECT_EQ(-1, controller.GetPendingEntryIndex()); 804 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex()); 805 EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL()); 806 } 807 808 // Tests navigating to a new URL when there is a pending back/forward 809 // navigation to a cross-process, privileged URL. This will happen if the user 810 // hits back, but before that commits, they navigate somewhere new. 811 TEST_F(NavigationControllerTest, LoadURL_PrivilegedPending) { 812 NavigationControllerImpl& controller = controller_impl(); 813 TestNotificationTracker notifications; 814 RegisterForAllNavNotifications(¬ifications, &controller); 815 816 // First make some history, starting with a privileged URL. 817 const GURL kExistingURL1("http://privileged"); 818 controller.LoadURL( 819 kExistingURL1, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 820 // Pretend it has bindings so we can tell if we incorrectly copy it. 821 test_rvh()->AllowBindings(2); 822 main_test_rfh()->SendNavigate(0, kExistingURL1); 823 EXPECT_EQ(1U, navigation_entry_committed_counter_); 824 navigation_entry_committed_counter_ = 0; 825 826 // Navigate cross-process to a second URL. 827 const GURL kExistingURL2("http://foo/eh"); 828 controller.LoadURL( 829 kExistingURL2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 830 test_rvh()->SendBeforeUnloadACK(true); 831 TestRenderViewHost* foo_rvh = static_cast<TestRenderViewHost*>( 832 contents()->GetPendingRenderViewHost()); 833 foo_rvh->SendNavigate(1, kExistingURL2); 834 EXPECT_EQ(1U, navigation_entry_committed_counter_); 835 navigation_entry_committed_counter_ = 0; 836 837 // Now make a pending back/forward navigation to a privileged entry. 838 // The zeroth entry should be pending. 839 controller.GoBack(); 840 foo_rvh->SendBeforeUnloadACK(true); 841 EXPECT_EQ(0U, notifications.size()); 842 EXPECT_EQ(0, controller.GetPendingEntryIndex()); 843 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex()); 844 EXPECT_EQ(2, NavigationEntryImpl::FromNavigationEntry( 845 controller.GetPendingEntry())->bindings()); 846 847 // Before that commits, do a new navigation. 848 const GURL kNewURL("http://foo/bee"); 849 LoadCommittedDetails details; 850 foo_rvh->SendNavigate(3, kNewURL); 851 852 // There should no longer be any pending entry, and the third navigation we 853 // just made should be committed. 854 EXPECT_EQ(1U, navigation_entry_committed_counter_); 855 navigation_entry_committed_counter_ = 0; 856 EXPECT_EQ(-1, controller.GetPendingEntryIndex()); 857 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex()); 858 EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL()); 859 EXPECT_EQ(0, NavigationEntryImpl::FromNavigationEntry( 860 controller.GetLastCommittedEntry())->bindings()); 861 } 862 863 // Tests navigating to an existing URL when there is a pending new navigation. 864 // This will happen if the user enters a URL, but before that commits, the 865 // current page fires history.back(). 866 TEST_F(NavigationControllerTest, LoadURL_BackPreemptsPending) { 867 NavigationControllerImpl& controller = controller_impl(); 868 TestNotificationTracker notifications; 869 RegisterForAllNavNotifications(¬ifications, &controller); 870 871 // First make some history. 872 const GURL kExistingURL1("http://foo/eh"); 873 controller.LoadURL( 874 kExistingURL1, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 875 main_test_rfh()->SendNavigate(0, kExistingURL1); 876 EXPECT_EQ(1U, navigation_entry_committed_counter_); 877 navigation_entry_committed_counter_ = 0; 878 879 const GURL kExistingURL2("http://foo/bee"); 880 controller.LoadURL( 881 kExistingURL2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 882 main_test_rfh()->SendNavigate(1, kExistingURL2); 883 EXPECT_EQ(1U, navigation_entry_committed_counter_); 884 navigation_entry_committed_counter_ = 0; 885 886 // Now make a pending new navigation. 887 const GURL kNewURL("http://foo/see"); 888 controller.LoadURL( 889 kNewURL, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 890 EXPECT_EQ(0U, notifications.size()); 891 EXPECT_EQ(-1, controller.GetPendingEntryIndex()); 892 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex()); 893 894 // Before that commits, a back navigation from the renderer commits. 895 main_test_rfh()->SendNavigate(0, kExistingURL1); 896 897 // There should no longer be any pending entry, and the back navigation we 898 // just made should be committed. 899 EXPECT_EQ(1U, navigation_entry_committed_counter_); 900 navigation_entry_committed_counter_ = 0; 901 EXPECT_EQ(-1, controller.GetPendingEntryIndex()); 902 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex()); 903 EXPECT_EQ(kExistingURL1, controller.GetVisibleEntry()->GetURL()); 904 } 905 906 // Tests an ignored navigation when there is a pending new navigation. 907 // This will happen if the user enters a URL, but before that commits, the 908 // current blank page reloads. See http://crbug.com/77507. 909 TEST_F(NavigationControllerTest, LoadURL_IgnorePreemptsPending) { 910 NavigationControllerImpl& controller = controller_impl(); 911 TestNotificationTracker notifications; 912 RegisterForAllNavNotifications(¬ifications, &controller); 913 914 // Set a WebContentsDelegate to listen for state changes. 915 scoped_ptr<TestWebContentsDelegate> delegate(new TestWebContentsDelegate()); 916 EXPECT_FALSE(contents()->GetDelegate()); 917 contents()->SetDelegate(delegate.get()); 918 919 // Without any navigations, the renderer starts at about:blank. 920 const GURL kExistingURL(url::kAboutBlankURL); 921 922 // Now make a pending new navigation. 923 const GURL kNewURL("http://eh"); 924 controller.LoadURL( 925 kNewURL, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 926 EXPECT_EQ(0U, notifications.size()); 927 EXPECT_EQ(-1, controller.GetPendingEntryIndex()); 928 EXPECT_TRUE(controller.GetPendingEntry()); 929 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex()); 930 EXPECT_EQ(1, delegate->navigation_state_change_count()); 931 932 // Before that commits, a document.write and location.reload can cause the 933 // renderer to send a FrameNavigate with page_id -1. 934 main_test_rfh()->SendNavigate(-1, kExistingURL); 935 936 // This should clear the pending entry and notify of a navigation state 937 // change, so that we do not keep displaying kNewURL. 938 EXPECT_EQ(-1, controller.GetPendingEntryIndex()); 939 EXPECT_FALSE(controller.GetPendingEntry()); 940 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex()); 941 EXPECT_EQ(2, delegate->navigation_state_change_count()); 942 943 contents()->SetDelegate(NULL); 944 } 945 946 // Tests that the pending entry state is correct after an abort. 947 // We do not want to clear the pending entry, so that the user doesn't 948 // lose a typed URL. (See http://crbug.com/9682.) 949 TEST_F(NavigationControllerTest, LoadURL_AbortDoesntCancelPending) { 950 NavigationControllerImpl& controller = controller_impl(); 951 TestNotificationTracker notifications; 952 RegisterForAllNavNotifications(¬ifications, &controller); 953 954 // Set a WebContentsDelegate to listen for state changes. 955 scoped_ptr<TestWebContentsDelegate> delegate(new TestWebContentsDelegate()); 956 EXPECT_FALSE(contents()->GetDelegate()); 957 contents()->SetDelegate(delegate.get()); 958 959 // Start with a pending new navigation. 960 const GURL kNewURL("http://eh"); 961 controller.LoadURL( 962 kNewURL, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 963 EXPECT_EQ(0U, notifications.size()); 964 EXPECT_EQ(-1, controller.GetPendingEntryIndex()); 965 EXPECT_TRUE(controller.GetPendingEntry()); 966 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex()); 967 EXPECT_EQ(1, delegate->navigation_state_change_count()); 968 969 // It may abort before committing, if it's a download or due to a stop or 970 // a new navigation from the user. 971 FrameHostMsg_DidFailProvisionalLoadWithError_Params params; 972 params.error_code = net::ERR_ABORTED; 973 params.error_description = base::string16(); 974 params.url = kNewURL; 975 params.showing_repost_interstitial = false; 976 main_test_rfh()->OnMessageReceived( 977 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id 978 params)); 979 980 // This should not clear the pending entry or notify of a navigation state 981 // change, so that we keep displaying kNewURL (until the user clears it). 982 EXPECT_EQ(-1, controller.GetPendingEntryIndex()); 983 EXPECT_TRUE(controller.GetPendingEntry()); 984 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex()); 985 EXPECT_EQ(1, delegate->navigation_state_change_count()); 986 NavigationEntry* pending_entry = controller.GetPendingEntry(); 987 988 // Ensure that a reload keeps the same pending entry. 989 controller.Reload(true); 990 EXPECT_EQ(-1, controller.GetPendingEntryIndex()); 991 EXPECT_TRUE(controller.GetPendingEntry()); 992 EXPECT_EQ(pending_entry, controller.GetPendingEntry()); 993 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex()); 994 995 contents()->SetDelegate(NULL); 996 } 997 998 // Tests that the pending URL is not visible during a renderer-initiated 999 // redirect and abort. See http://crbug.com/83031. 1000 TEST_F(NavigationControllerTest, LoadURL_RedirectAbortDoesntShowPendingURL) { 1001 NavigationControllerImpl& controller = controller_impl(); 1002 TestNotificationTracker notifications; 1003 RegisterForAllNavNotifications(¬ifications, &controller); 1004 1005 // First make an existing committed entry. 1006 const GURL kExistingURL("http://foo/eh"); 1007 controller.LoadURL(kExistingURL, content::Referrer(), 1008 content::PAGE_TRANSITION_TYPED, std::string()); 1009 main_test_rfh()->SendNavigate(0, kExistingURL); 1010 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1011 navigation_entry_committed_counter_ = 0; 1012 1013 // Set a WebContentsDelegate to listen for state changes. 1014 scoped_ptr<TestWebContentsDelegate> delegate(new TestWebContentsDelegate()); 1015 EXPECT_FALSE(contents()->GetDelegate()); 1016 contents()->SetDelegate(delegate.get()); 1017 1018 // Now make a pending new navigation, initiated by the renderer. 1019 const GURL kNewURL("http://foo/bee"); 1020 NavigationController::LoadURLParams load_url_params(kNewURL); 1021 load_url_params.transition_type = PAGE_TRANSITION_TYPED; 1022 load_url_params.is_renderer_initiated = true; 1023 controller.LoadURLWithParams(load_url_params); 1024 EXPECT_EQ(0U, notifications.size()); 1025 EXPECT_EQ(-1, controller.GetPendingEntryIndex()); 1026 EXPECT_TRUE(controller.GetPendingEntry()); 1027 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex()); 1028 EXPECT_EQ(0, delegate->navigation_state_change_count()); 1029 1030 // The visible entry should be the last committed URL, not the pending one. 1031 EXPECT_EQ(kExistingURL, controller.GetVisibleEntry()->GetURL()); 1032 1033 // Now the navigation redirects. 1034 const GURL kRedirectURL("http://foo/see"); 1035 main_test_rfh()->OnMessageReceived( 1036 FrameHostMsg_DidRedirectProvisionalLoad(0, // routing_id 1037 -1, // pending page_id 1038 kNewURL, // old url 1039 kRedirectURL)); // new url 1040 1041 // We don't want to change the NavigationEntry's url, in case it cancels. 1042 // Prevents regression of http://crbug.com/77786. 1043 EXPECT_EQ(kNewURL, controller.GetPendingEntry()->GetURL()); 1044 1045 // It may abort before committing, if it's a download or due to a stop or 1046 // a new navigation from the user. 1047 FrameHostMsg_DidFailProvisionalLoadWithError_Params params; 1048 params.error_code = net::ERR_ABORTED; 1049 params.error_description = base::string16(); 1050 params.url = kRedirectURL; 1051 params.showing_repost_interstitial = false; 1052 main_test_rfh()->OnMessageReceived( 1053 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id 1054 params)); 1055 1056 // Because the pending entry is renderer initiated and not visible, we 1057 // clear it when it fails. 1058 EXPECT_EQ(-1, controller.GetPendingEntryIndex()); 1059 EXPECT_FALSE(controller.GetPendingEntry()); 1060 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex()); 1061 EXPECT_EQ(1, delegate->navigation_state_change_count()); 1062 1063 // The visible entry should be the last committed URL, not the pending one, 1064 // so that no spoof is possible. 1065 EXPECT_EQ(kExistingURL, controller.GetVisibleEntry()->GetURL()); 1066 1067 contents()->SetDelegate(NULL); 1068 } 1069 1070 // Ensure that NavigationEntries track which bindings their RenderViewHost had 1071 // at the time they committed. http://crbug.com/173672. 1072 TEST_F(NavigationControllerTest, LoadURL_WithBindings) { 1073 NavigationControllerImpl& controller = controller_impl(); 1074 TestNotificationTracker notifications; 1075 RegisterForAllNavNotifications(¬ifications, &controller); 1076 std::vector<GURL> url_chain; 1077 1078 const GURL url1("http://foo1"); 1079 const GURL url2("http://foo2"); 1080 1081 // Navigate to a first, unprivileged URL. 1082 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 1083 EXPECT_EQ(NavigationEntryImpl::kInvalidBindings, 1084 NavigationEntryImpl::FromNavigationEntry( 1085 controller.GetPendingEntry())->bindings()); 1086 1087 // Commit. 1088 TestRenderViewHost* orig_rvh = static_cast<TestRenderViewHost*>(test_rvh()); 1089 orig_rvh->SendNavigate(0, url1); 1090 EXPECT_EQ(controller.GetEntryCount(), 1); 1091 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex()); 1092 EXPECT_EQ(0, NavigationEntryImpl::FromNavigationEntry( 1093 controller.GetLastCommittedEntry())->bindings()); 1094 1095 // Manually increase the number of active views in the SiteInstance 1096 // that orig_rvh belongs to, to prevent it from being destroyed when 1097 // it gets swapped out, so that we can reuse orig_rvh when the 1098 // controller goes back. 1099 static_cast<SiteInstanceImpl*>(orig_rvh->GetSiteInstance())-> 1100 increment_active_view_count(); 1101 1102 // Navigate to a second URL, simulate the beforeunload ack for the cross-site 1103 // transition, run the unload handler, and set bindings on the pending 1104 // RenderViewHost to simulate a privileged url. 1105 controller.LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 1106 orig_rvh->SendBeforeUnloadACK(true); 1107 contents()->GetRenderManagerForTesting()->OnCrossSiteResponse( 1108 contents()->GetRenderManagerForTesting()->pending_frame_host(), 1109 GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(), 1110 url_chain, Referrer(), PAGE_TRANSITION_TYPED, false); 1111 TestRenderViewHost* new_rvh = 1112 static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost()); 1113 new_rvh->AllowBindings(1); 1114 new_rvh->SendNavigate(1, url2); 1115 1116 // The second load should be committed, and bindings should be remembered. 1117 EXPECT_EQ(controller.GetEntryCount(), 2); 1118 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex()); 1119 EXPECT_TRUE(controller.CanGoBack()); 1120 EXPECT_EQ(1, NavigationEntryImpl::FromNavigationEntry( 1121 controller.GetLastCommittedEntry())->bindings()); 1122 1123 // Going back, the first entry should still appear unprivileged. 1124 controller.GoBack(); 1125 new_rvh->SendBeforeUnloadACK(true); 1126 contents()->GetRenderManagerForTesting()->OnCrossSiteResponse( 1127 contents()->GetRenderManagerForTesting()->pending_frame_host(), 1128 GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(), 1129 url_chain, Referrer(), PAGE_TRANSITION_TYPED, false); 1130 orig_rvh->SendNavigate(0, url1); 1131 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex()); 1132 EXPECT_EQ(0, NavigationEntryImpl::FromNavigationEntry( 1133 controller.GetLastCommittedEntry())->bindings()); 1134 } 1135 1136 TEST_F(NavigationControllerTest, Reload) { 1137 NavigationControllerImpl& controller = controller_impl(); 1138 TestNotificationTracker notifications; 1139 RegisterForAllNavNotifications(¬ifications, &controller); 1140 1141 const GURL url1("http://foo1"); 1142 1143 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 1144 EXPECT_EQ(0U, notifications.size()); 1145 main_test_rfh()->SendNavigate(0, url1); 1146 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1147 navigation_entry_committed_counter_ = 0; 1148 ASSERT_TRUE(controller.GetVisibleEntry()); 1149 controller.GetVisibleEntry()->SetTitle(base::ASCIIToUTF16("Title")); 1150 controller.Reload(true); 1151 EXPECT_EQ(0U, notifications.size()); 1152 1153 const base::Time timestamp = controller.GetVisibleEntry()->GetTimestamp(); 1154 EXPECT_FALSE(timestamp.is_null()); 1155 1156 // The reload is pending. 1157 EXPECT_EQ(controller.GetEntryCount(), 1); 1158 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0); 1159 EXPECT_EQ(controller.GetPendingEntryIndex(), 0); 1160 EXPECT_TRUE(controller.GetLastCommittedEntry()); 1161 EXPECT_TRUE(controller.GetPendingEntry()); 1162 EXPECT_FALSE(controller.CanGoBack()); 1163 EXPECT_FALSE(controller.CanGoForward()); 1164 // Make sure the title has been cleared (will be redrawn just after reload). 1165 // Avoids a stale cached title when the new page being reloaded has no title. 1166 // See http://crbug.com/96041. 1167 EXPECT_TRUE(controller.GetVisibleEntry()->GetTitle().empty()); 1168 1169 main_test_rfh()->SendNavigate(0, url1); 1170 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1171 navigation_entry_committed_counter_ = 0; 1172 1173 // Now the reload is committed. 1174 EXPECT_EQ(controller.GetEntryCount(), 1); 1175 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0); 1176 EXPECT_EQ(controller.GetPendingEntryIndex(), -1); 1177 EXPECT_TRUE(controller.GetLastCommittedEntry()); 1178 EXPECT_FALSE(controller.GetPendingEntry()); 1179 EXPECT_FALSE(controller.CanGoBack()); 1180 EXPECT_FALSE(controller.CanGoForward()); 1181 1182 // The timestamp should have been updated. 1183 ASSERT_TRUE(controller.GetVisibleEntry()); 1184 EXPECT_GE(controller.GetVisibleEntry()->GetTimestamp(), timestamp); 1185 } 1186 1187 // Tests what happens when a reload navigation produces a new page. 1188 TEST_F(NavigationControllerTest, Reload_GeneratesNewPage) { 1189 NavigationControllerImpl& controller = controller_impl(); 1190 TestNotificationTracker notifications; 1191 RegisterForAllNavNotifications(¬ifications, &controller); 1192 1193 const GURL url1("http://foo1"); 1194 const GURL url2("http://foo2"); 1195 1196 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 1197 main_test_rfh()->SendNavigate(0, url1); 1198 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1199 navigation_entry_committed_counter_ = 0; 1200 1201 controller.Reload(true); 1202 EXPECT_EQ(0U, notifications.size()); 1203 1204 main_test_rfh()->SendNavigate(1, url2); 1205 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1206 navigation_entry_committed_counter_ = 0; 1207 1208 // Now the reload is committed. 1209 EXPECT_EQ(controller.GetEntryCount(), 2); 1210 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1); 1211 EXPECT_EQ(controller.GetPendingEntryIndex(), -1); 1212 EXPECT_TRUE(controller.GetLastCommittedEntry()); 1213 EXPECT_FALSE(controller.GetPendingEntry()); 1214 EXPECT_TRUE(controller.CanGoBack()); 1215 EXPECT_FALSE(controller.CanGoForward()); 1216 } 1217 1218 // This test ensures that when a guest renderer reloads, the reload goes through 1219 // without ending up in the "we have a wrong process for the URL" branch in 1220 // NavigationControllerImpl::ReloadInternal. 1221 TEST_F(NavigationControllerTest, ReloadWithGuest) { 1222 NavigationControllerImpl& controller = controller_impl(); 1223 1224 const GURL url1("http://foo1"); 1225 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 1226 main_test_rfh()->SendNavigate(0, url1); 1227 ASSERT_TRUE(controller.GetVisibleEntry()); 1228 1229 // Make the entry believe its RenderProcessHost is a guest. 1230 NavigationEntryImpl* entry1 = 1231 NavigationEntryImpl::FromNavigationEntry(controller.GetVisibleEntry()); 1232 reinterpret_cast<MockRenderProcessHost*>( 1233 entry1->site_instance()->GetProcess())->set_is_isolated_guest(true); 1234 1235 // And reload. 1236 controller.Reload(true); 1237 1238 // The reload is pending. Check that the NavigationEntry didn't get replaced 1239 // because of having the wrong process. 1240 EXPECT_EQ(controller.GetEntryCount(), 1); 1241 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0); 1242 EXPECT_EQ(controller.GetPendingEntryIndex(), 0); 1243 1244 NavigationEntryImpl* entry2 = 1245 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry()); 1246 EXPECT_EQ(entry1, entry2); 1247 } 1248 1249 #if !defined(OS_ANDROID) // http://crbug.com/157428 1250 TEST_F(NavigationControllerTest, ReloadOriginalRequestURL) { 1251 NavigationControllerImpl& controller = controller_impl(); 1252 TestNotificationTracker notifications; 1253 RegisterForAllNavNotifications(¬ifications, &controller); 1254 1255 const GURL original_url("http://foo1"); 1256 const GURL final_url("http://foo2"); 1257 1258 // Load up the original URL, but get redirected. 1259 controller.LoadURL( 1260 original_url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 1261 EXPECT_EQ(0U, notifications.size()); 1262 main_test_rfh()->SendNavigateWithOriginalRequestURL( 1263 0, final_url, original_url); 1264 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1265 navigation_entry_committed_counter_ = 0; 1266 1267 // The NavigationEntry should save both the original URL and the final 1268 // redirected URL. 1269 EXPECT_EQ( 1270 original_url, controller.GetVisibleEntry()->GetOriginalRequestURL()); 1271 EXPECT_EQ(final_url, controller.GetVisibleEntry()->GetURL()); 1272 1273 // Reload using the original URL. 1274 controller.GetVisibleEntry()->SetTitle(base::ASCIIToUTF16("Title")); 1275 controller.ReloadOriginalRequestURL(false); 1276 EXPECT_EQ(0U, notifications.size()); 1277 1278 // The reload is pending. The request should point to the original URL. 1279 EXPECT_EQ(original_url, navigated_url()); 1280 EXPECT_EQ(controller.GetEntryCount(), 1); 1281 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0); 1282 EXPECT_EQ(controller.GetPendingEntryIndex(), 0); 1283 EXPECT_TRUE(controller.GetLastCommittedEntry()); 1284 EXPECT_TRUE(controller.GetPendingEntry()); 1285 EXPECT_FALSE(controller.CanGoBack()); 1286 EXPECT_FALSE(controller.CanGoForward()); 1287 1288 // Make sure the title has been cleared (will be redrawn just after reload). 1289 // Avoids a stale cached title when the new page being reloaded has no title. 1290 // See http://crbug.com/96041. 1291 EXPECT_TRUE(controller.GetVisibleEntry()->GetTitle().empty()); 1292 1293 // Send that the navigation has proceeded; say it got redirected again. 1294 main_test_rfh()->SendNavigate(0, final_url); 1295 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1296 navigation_entry_committed_counter_ = 0; 1297 1298 // Now the reload is committed. 1299 EXPECT_EQ(controller.GetEntryCount(), 1); 1300 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0); 1301 EXPECT_EQ(controller.GetPendingEntryIndex(), -1); 1302 EXPECT_TRUE(controller.GetLastCommittedEntry()); 1303 EXPECT_FALSE(controller.GetPendingEntry()); 1304 EXPECT_FALSE(controller.CanGoBack()); 1305 EXPECT_FALSE(controller.CanGoForward()); 1306 } 1307 1308 #endif // !defined(OS_ANDROID) 1309 1310 // Test that certain non-persisted NavigationEntryImpl values get reset after 1311 // commit. 1312 TEST_F(NavigationControllerTest, ResetEntryValuesAfterCommit) { 1313 NavigationControllerImpl& controller = controller_impl(); 1314 const GURL url1("http://foo1"); 1315 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 1316 1317 // Set up some sample values. 1318 const unsigned char* raw_data = 1319 reinterpret_cast<const unsigned char*>("post\n\n\0data"); 1320 const int length = 11; 1321 std::vector<unsigned char> post_data_vector(raw_data, raw_data+length); 1322 scoped_refptr<base::RefCountedBytes> post_data = 1323 base::RefCountedBytes::TakeVector(&post_data_vector); 1324 GlobalRequestID transfer_id(3, 4); 1325 std::vector<GURL> redirects; 1326 redirects.push_back(GURL("http://foo2")); 1327 1328 // Set non-persisted values on the pending entry. 1329 NavigationEntryImpl* pending_entry = 1330 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry()); 1331 pending_entry->SetBrowserInitiatedPostData(post_data.get()); 1332 pending_entry->set_is_renderer_initiated(true); 1333 pending_entry->set_transferred_global_request_id(transfer_id); 1334 pending_entry->set_should_replace_entry(true); 1335 pending_entry->set_should_clear_history_list(true); 1336 EXPECT_EQ(post_data.get(), pending_entry->GetBrowserInitiatedPostData()); 1337 EXPECT_TRUE(pending_entry->is_renderer_initiated()); 1338 EXPECT_EQ(transfer_id, pending_entry->transferred_global_request_id()); 1339 EXPECT_TRUE(pending_entry->should_replace_entry()); 1340 EXPECT_TRUE(pending_entry->should_clear_history_list()); 1341 1342 main_test_rfh()->SendNavigate(0, url1); 1343 1344 // Certain values that are only used for pending entries get reset after 1345 // commit. 1346 NavigationEntryImpl* committed_entry = 1347 NavigationEntryImpl::FromNavigationEntry( 1348 controller.GetLastCommittedEntry()); 1349 EXPECT_FALSE(committed_entry->GetBrowserInitiatedPostData()); 1350 EXPECT_FALSE(committed_entry->is_renderer_initiated()); 1351 EXPECT_EQ(GlobalRequestID(-1, -1), 1352 committed_entry->transferred_global_request_id()); 1353 EXPECT_FALSE(committed_entry->should_replace_entry()); 1354 EXPECT_FALSE(committed_entry->should_clear_history_list()); 1355 } 1356 1357 // Test that Redirects are preserved after a commit. 1358 TEST_F(NavigationControllerTest, RedirectsAreNotResetByCommit) { 1359 NavigationControllerImpl& controller = controller_impl(); 1360 const GURL url1("http://foo1"); 1361 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 1362 1363 // Set up some redirect values. 1364 std::vector<GURL> redirects; 1365 redirects.push_back(GURL("http://foo2")); 1366 1367 // Set redirects on the pending entry. 1368 NavigationEntryImpl* pending_entry = 1369 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry()); 1370 pending_entry->SetRedirectChain(redirects); 1371 EXPECT_EQ(1U, pending_entry->GetRedirectChain().size()); 1372 EXPECT_EQ(GURL("http://foo2"), pending_entry->GetRedirectChain()[0]); 1373 1374 // Normal navigation will preserve redirects in the committed entry. 1375 main_test_rfh()->SendNavigateWithRedirects(0, url1, redirects); 1376 NavigationEntryImpl* committed_entry = 1377 NavigationEntryImpl::FromNavigationEntry( 1378 controller.GetLastCommittedEntry()); 1379 ASSERT_EQ(1U, committed_entry->GetRedirectChain().size()); 1380 EXPECT_EQ(GURL("http://foo2"), committed_entry->GetRedirectChain()[0]); 1381 } 1382 1383 // Tests what happens when we navigate back successfully 1384 TEST_F(NavigationControllerTest, Back) { 1385 NavigationControllerImpl& controller = controller_impl(); 1386 TestNotificationTracker notifications; 1387 RegisterForAllNavNotifications(¬ifications, &controller); 1388 1389 const GURL url1("http://foo1"); 1390 main_test_rfh()->SendNavigate(0, url1); 1391 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1392 navigation_entry_committed_counter_ = 0; 1393 1394 const GURL url2("http://foo2"); 1395 main_test_rfh()->SendNavigate(1, url2); 1396 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1397 navigation_entry_committed_counter_ = 0; 1398 1399 controller.GoBack(); 1400 EXPECT_EQ(0U, notifications.size()); 1401 1402 // We should now have a pending navigation to go back. 1403 EXPECT_EQ(controller.GetEntryCount(), 2); 1404 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1); 1405 EXPECT_EQ(controller.GetPendingEntryIndex(), 0); 1406 EXPECT_TRUE(controller.GetLastCommittedEntry()); 1407 EXPECT_TRUE(controller.GetPendingEntry()); 1408 EXPECT_FALSE(controller.CanGoBack()); 1409 EXPECT_FALSE(controller.CanGoToOffset(-1)); 1410 EXPECT_TRUE(controller.CanGoForward()); 1411 EXPECT_TRUE(controller.CanGoToOffset(1)); 1412 EXPECT_FALSE(controller.CanGoToOffset(2)); // Cannot go foward 2 steps. 1413 1414 // Timestamp for entry 1 should be on or after that of entry 0. 1415 EXPECT_FALSE(controller.GetEntryAtIndex(0)->GetTimestamp().is_null()); 1416 EXPECT_GE(controller.GetEntryAtIndex(1)->GetTimestamp(), 1417 controller.GetEntryAtIndex(0)->GetTimestamp()); 1418 1419 main_test_rfh()->SendNavigate(0, url2); 1420 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1421 navigation_entry_committed_counter_ = 0; 1422 1423 // The back navigation completed successfully. 1424 EXPECT_EQ(controller.GetEntryCount(), 2); 1425 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0); 1426 EXPECT_EQ(controller.GetPendingEntryIndex(), -1); 1427 EXPECT_TRUE(controller.GetLastCommittedEntry()); 1428 EXPECT_FALSE(controller.GetPendingEntry()); 1429 EXPECT_FALSE(controller.CanGoBack()); 1430 EXPECT_FALSE(controller.CanGoToOffset(-1)); 1431 EXPECT_TRUE(controller.CanGoForward()); 1432 EXPECT_TRUE(controller.CanGoToOffset(1)); 1433 EXPECT_FALSE(controller.CanGoToOffset(2)); // Cannot go foward 2 steps. 1434 1435 // Timestamp for entry 0 should be on or after that of entry 1 1436 // (since we went back to it). 1437 EXPECT_GE(controller.GetEntryAtIndex(0)->GetTimestamp(), 1438 controller.GetEntryAtIndex(1)->GetTimestamp()); 1439 } 1440 1441 // Tests what happens when a back navigation produces a new page. 1442 TEST_F(NavigationControllerTest, Back_GeneratesNewPage) { 1443 NavigationControllerImpl& controller = controller_impl(); 1444 TestNotificationTracker notifications; 1445 RegisterForAllNavNotifications(¬ifications, &controller); 1446 1447 const GURL url1("http://foo/1"); 1448 const GURL url2("http://foo/2"); 1449 const GURL url3("http://foo/3"); 1450 1451 controller.LoadURL( 1452 url1, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 1453 main_test_rfh()->SendNavigate(0, url1); 1454 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1455 navigation_entry_committed_counter_ = 0; 1456 1457 controller.LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 1458 main_test_rfh()->SendNavigate(1, url2); 1459 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1460 navigation_entry_committed_counter_ = 0; 1461 1462 controller.GoBack(); 1463 EXPECT_EQ(0U, notifications.size()); 1464 1465 // We should now have a pending navigation to go back. 1466 EXPECT_EQ(controller.GetEntryCount(), 2); 1467 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1); 1468 EXPECT_EQ(controller.GetPendingEntryIndex(), 0); 1469 EXPECT_TRUE(controller.GetLastCommittedEntry()); 1470 EXPECT_TRUE(controller.GetPendingEntry()); 1471 EXPECT_FALSE(controller.CanGoBack()); 1472 EXPECT_TRUE(controller.CanGoForward()); 1473 1474 main_test_rfh()->SendNavigate(2, url3); 1475 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1476 navigation_entry_committed_counter_ = 0; 1477 1478 // The back navigation resulted in a completely new navigation. 1479 // TODO(darin): perhaps this behavior will be confusing to users? 1480 EXPECT_EQ(controller.GetEntryCount(), 3); 1481 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 2); 1482 EXPECT_EQ(controller.GetPendingEntryIndex(), -1); 1483 EXPECT_TRUE(controller.GetLastCommittedEntry()); 1484 EXPECT_FALSE(controller.GetPendingEntry()); 1485 EXPECT_TRUE(controller.CanGoBack()); 1486 EXPECT_FALSE(controller.CanGoForward()); 1487 } 1488 1489 // Receives a back message when there is a new pending navigation entry. 1490 TEST_F(NavigationControllerTest, Back_NewPending) { 1491 NavigationControllerImpl& controller = controller_impl(); 1492 TestNotificationTracker notifications; 1493 RegisterForAllNavNotifications(¬ifications, &controller); 1494 1495 const GURL kUrl1("http://foo1"); 1496 const GURL kUrl2("http://foo2"); 1497 const GURL kUrl3("http://foo3"); 1498 1499 // First navigate two places so we have some back history. 1500 main_test_rfh()->SendNavigate(0, kUrl1); 1501 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1502 navigation_entry_committed_counter_ = 0; 1503 1504 // controller.LoadURL(kUrl2, PAGE_TRANSITION_TYPED); 1505 main_test_rfh()->SendNavigate(1, kUrl2); 1506 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1507 navigation_entry_committed_counter_ = 0; 1508 1509 // Now start a new pending navigation and go back before it commits. 1510 controller.LoadURL(kUrl3, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 1511 EXPECT_EQ(-1, controller.GetPendingEntryIndex()); 1512 EXPECT_EQ(kUrl3, controller.GetPendingEntry()->GetURL()); 1513 controller.GoBack(); 1514 1515 // The pending navigation should now be the "back" item and the new one 1516 // should be gone. 1517 EXPECT_EQ(0, controller.GetPendingEntryIndex()); 1518 EXPECT_EQ(kUrl1, controller.GetPendingEntry()->GetURL()); 1519 } 1520 1521 // Receives a back message when there is a different renavigation already 1522 // pending. 1523 TEST_F(NavigationControllerTest, Back_OtherBackPending) { 1524 NavigationControllerImpl& controller = controller_impl(); 1525 const GURL kUrl1("http://foo/1"); 1526 const GURL kUrl2("http://foo/2"); 1527 const GURL kUrl3("http://foo/3"); 1528 1529 // First navigate three places so we have some back history. 1530 main_test_rfh()->SendNavigate(0, kUrl1); 1531 main_test_rfh()->SendNavigate(1, kUrl2); 1532 main_test_rfh()->SendNavigate(2, kUrl3); 1533 1534 // With nothing pending, say we get a navigation to the second entry. 1535 main_test_rfh()->SendNavigate(1, kUrl2); 1536 1537 // We know all the entries have the same site instance, so we can just grab 1538 // a random one for looking up other entries. 1539 SiteInstance* site_instance = 1540 NavigationEntryImpl::FromNavigationEntry( 1541 controller.GetLastCommittedEntry())->site_instance(); 1542 1543 // That second URL should be the last committed and it should have gotten the 1544 // new title. 1545 EXPECT_EQ(kUrl2, controller.GetEntryWithPageID(site_instance, 1)->GetURL()); 1546 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex()); 1547 EXPECT_EQ(-1, controller.GetPendingEntryIndex()); 1548 1549 // Now go forward to the last item again and say it was committed. 1550 controller.GoForward(); 1551 main_test_rfh()->SendNavigate(2, kUrl3); 1552 1553 // Now start going back one to the second page. It will be pending. 1554 controller.GoBack(); 1555 EXPECT_EQ(1, controller.GetPendingEntryIndex()); 1556 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex()); 1557 1558 // Not synthesize a totally new back event to the first page. This will not 1559 // match the pending one. 1560 main_test_rfh()->SendNavigate(0, kUrl1); 1561 1562 // The committed navigation should clear the pending entry. 1563 EXPECT_EQ(-1, controller.GetPendingEntryIndex()); 1564 1565 // But the navigated entry should be the last committed. 1566 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex()); 1567 EXPECT_EQ(kUrl1, controller.GetLastCommittedEntry()->GetURL()); 1568 } 1569 1570 // Tests what happens when we navigate forward successfully. 1571 TEST_F(NavigationControllerTest, Forward) { 1572 NavigationControllerImpl& controller = controller_impl(); 1573 TestNotificationTracker notifications; 1574 RegisterForAllNavNotifications(¬ifications, &controller); 1575 1576 const GURL url1("http://foo1"); 1577 const GURL url2("http://foo2"); 1578 1579 main_test_rfh()->SendNavigate(0, url1); 1580 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1581 navigation_entry_committed_counter_ = 0; 1582 1583 main_test_rfh()->SendNavigate(1, url2); 1584 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1585 navigation_entry_committed_counter_ = 0; 1586 1587 controller.GoBack(); 1588 main_test_rfh()->SendNavigate(0, url1); 1589 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1590 navigation_entry_committed_counter_ = 0; 1591 1592 controller.GoForward(); 1593 1594 // We should now have a pending navigation to go forward. 1595 EXPECT_EQ(controller.GetEntryCount(), 2); 1596 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0); 1597 EXPECT_EQ(controller.GetPendingEntryIndex(), 1); 1598 EXPECT_TRUE(controller.GetLastCommittedEntry()); 1599 EXPECT_TRUE(controller.GetPendingEntry()); 1600 EXPECT_TRUE(controller.CanGoBack()); 1601 EXPECT_TRUE(controller.CanGoToOffset(-1)); 1602 EXPECT_FALSE(controller.CanGoToOffset(-2)); // Cannot go back 2 steps. 1603 EXPECT_FALSE(controller.CanGoForward()); 1604 EXPECT_FALSE(controller.CanGoToOffset(1)); 1605 1606 // Timestamp for entry 0 should be on or after that of entry 1 1607 // (since we went back to it). 1608 EXPECT_FALSE(controller.GetEntryAtIndex(0)->GetTimestamp().is_null()); 1609 EXPECT_GE(controller.GetEntryAtIndex(0)->GetTimestamp(), 1610 controller.GetEntryAtIndex(1)->GetTimestamp()); 1611 1612 main_test_rfh()->SendNavigate(1, url2); 1613 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1614 navigation_entry_committed_counter_ = 0; 1615 1616 // The forward navigation completed successfully. 1617 EXPECT_EQ(controller.GetEntryCount(), 2); 1618 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1); 1619 EXPECT_EQ(controller.GetPendingEntryIndex(), -1); 1620 EXPECT_TRUE(controller.GetLastCommittedEntry()); 1621 EXPECT_FALSE(controller.GetPendingEntry()); 1622 EXPECT_TRUE(controller.CanGoBack()); 1623 EXPECT_TRUE(controller.CanGoToOffset(-1)); 1624 EXPECT_FALSE(controller.CanGoToOffset(-2)); // Cannot go back 2 steps. 1625 EXPECT_FALSE(controller.CanGoForward()); 1626 EXPECT_FALSE(controller.CanGoToOffset(1)); 1627 1628 // Timestamp for entry 1 should be on or after that of entry 0 1629 // (since we went forward to it). 1630 EXPECT_GE(controller.GetEntryAtIndex(1)->GetTimestamp(), 1631 controller.GetEntryAtIndex(0)->GetTimestamp()); 1632 } 1633 1634 // Tests what happens when a forward navigation produces a new page. 1635 TEST_F(NavigationControllerTest, Forward_GeneratesNewPage) { 1636 NavigationControllerImpl& controller = controller_impl(); 1637 TestNotificationTracker notifications; 1638 RegisterForAllNavNotifications(¬ifications, &controller); 1639 1640 const GURL url1("http://foo1"); 1641 const GURL url2("http://foo2"); 1642 const GURL url3("http://foo3"); 1643 1644 main_test_rfh()->SendNavigate(0, url1); 1645 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1646 navigation_entry_committed_counter_ = 0; 1647 main_test_rfh()->SendNavigate(1, url2); 1648 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1649 navigation_entry_committed_counter_ = 0; 1650 1651 controller.GoBack(); 1652 main_test_rfh()->SendNavigate(0, url1); 1653 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1654 navigation_entry_committed_counter_ = 0; 1655 1656 controller.GoForward(); 1657 EXPECT_EQ(0U, notifications.size()); 1658 1659 // Should now have a pending navigation to go forward. 1660 EXPECT_EQ(controller.GetEntryCount(), 2); 1661 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0); 1662 EXPECT_EQ(controller.GetPendingEntryIndex(), 1); 1663 EXPECT_TRUE(controller.GetLastCommittedEntry()); 1664 EXPECT_TRUE(controller.GetPendingEntry()); 1665 EXPECT_TRUE(controller.CanGoBack()); 1666 EXPECT_FALSE(controller.CanGoForward()); 1667 1668 main_test_rfh()->SendNavigate(2, url3); 1669 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1670 navigation_entry_committed_counter_ = 0; 1671 EXPECT_TRUE(notifications.Check1AndReset(NOTIFICATION_NAV_LIST_PRUNED)); 1672 1673 EXPECT_EQ(controller.GetEntryCount(), 2); 1674 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1); 1675 EXPECT_EQ(controller.GetPendingEntryIndex(), -1); 1676 EXPECT_TRUE(controller.GetLastCommittedEntry()); 1677 EXPECT_FALSE(controller.GetPendingEntry()); 1678 EXPECT_TRUE(controller.CanGoBack()); 1679 EXPECT_FALSE(controller.CanGoForward()); 1680 } 1681 1682 // Two consequent navigation for the same URL entered in should be considered 1683 // as SAME_PAGE navigation even when we are redirected to some other page. 1684 TEST_F(NavigationControllerTest, Redirect) { 1685 NavigationControllerImpl& controller = controller_impl(); 1686 TestNotificationTracker notifications; 1687 RegisterForAllNavNotifications(¬ifications, &controller); 1688 1689 const GURL url1("http://foo1"); 1690 const GURL url2("http://foo2"); // Redirection target 1691 1692 // First request 1693 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 1694 1695 EXPECT_EQ(0U, notifications.size()); 1696 main_test_rfh()->SendNavigate(0, url2); 1697 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1698 navigation_entry_committed_counter_ = 0; 1699 1700 // Second request 1701 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 1702 1703 EXPECT_TRUE(controller.GetPendingEntry()); 1704 EXPECT_EQ(controller.GetPendingEntryIndex(), -1); 1705 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL()); 1706 1707 FrameHostMsg_DidCommitProvisionalLoad_Params params; 1708 params.page_id = 0; 1709 params.url = url2; 1710 params.transition = PAGE_TRANSITION_SERVER_REDIRECT; 1711 params.redirects.push_back(GURL("http://foo1")); 1712 params.redirects.push_back(GURL("http://foo2")); 1713 params.should_update_history = false; 1714 params.gesture = NavigationGestureAuto; 1715 params.is_post = false; 1716 params.page_state = PageState::CreateFromURL(url2); 1717 1718 LoadCommittedDetails details; 1719 1720 EXPECT_EQ(0U, notifications.size()); 1721 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params, 1722 &details)); 1723 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1724 navigation_entry_committed_counter_ = 0; 1725 1726 EXPECT_TRUE(details.type == NAVIGATION_TYPE_SAME_PAGE); 1727 EXPECT_EQ(controller.GetEntryCount(), 1); 1728 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0); 1729 EXPECT_TRUE(controller.GetLastCommittedEntry()); 1730 EXPECT_EQ(controller.GetPendingEntryIndex(), -1); 1731 EXPECT_FALSE(controller.GetPendingEntry()); 1732 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL()); 1733 1734 EXPECT_FALSE(controller.CanGoBack()); 1735 EXPECT_FALSE(controller.CanGoForward()); 1736 } 1737 1738 // Similar to Redirect above, but the first URL is requested by POST, 1739 // the second URL is requested by GET. NavigationEntry::has_post_data_ 1740 // must be cleared. http://crbug.com/21245 1741 TEST_F(NavigationControllerTest, PostThenRedirect) { 1742 NavigationControllerImpl& controller = controller_impl(); 1743 TestNotificationTracker notifications; 1744 RegisterForAllNavNotifications(¬ifications, &controller); 1745 1746 const GURL url1("http://foo1"); 1747 const GURL url2("http://foo2"); // Redirection target 1748 1749 // First request as POST 1750 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 1751 controller.GetVisibleEntry()->SetHasPostData(true); 1752 1753 EXPECT_EQ(0U, notifications.size()); 1754 main_test_rfh()->SendNavigate(0, url2); 1755 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1756 navigation_entry_committed_counter_ = 0; 1757 1758 // Second request 1759 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 1760 1761 EXPECT_TRUE(controller.GetPendingEntry()); 1762 EXPECT_EQ(controller.GetPendingEntryIndex(), -1); 1763 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL()); 1764 1765 FrameHostMsg_DidCommitProvisionalLoad_Params params; 1766 params.page_id = 0; 1767 params.url = url2; 1768 params.transition = PAGE_TRANSITION_SERVER_REDIRECT; 1769 params.redirects.push_back(GURL("http://foo1")); 1770 params.redirects.push_back(GURL("http://foo2")); 1771 params.should_update_history = false; 1772 params.gesture = NavigationGestureAuto; 1773 params.is_post = false; 1774 params.page_state = PageState::CreateFromURL(url2); 1775 1776 LoadCommittedDetails details; 1777 1778 EXPECT_EQ(0U, notifications.size()); 1779 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params, 1780 &details)); 1781 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1782 navigation_entry_committed_counter_ = 0; 1783 1784 EXPECT_TRUE(details.type == NAVIGATION_TYPE_SAME_PAGE); 1785 EXPECT_EQ(controller.GetEntryCount(), 1); 1786 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0); 1787 EXPECT_TRUE(controller.GetLastCommittedEntry()); 1788 EXPECT_EQ(controller.GetPendingEntryIndex(), -1); 1789 EXPECT_FALSE(controller.GetPendingEntry()); 1790 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL()); 1791 EXPECT_FALSE(controller.GetVisibleEntry()->GetHasPostData()); 1792 1793 EXPECT_FALSE(controller.CanGoBack()); 1794 EXPECT_FALSE(controller.CanGoForward()); 1795 } 1796 1797 // A redirect right off the bat should be a NEW_PAGE. 1798 TEST_F(NavigationControllerTest, ImmediateRedirect) { 1799 NavigationControllerImpl& controller = controller_impl(); 1800 TestNotificationTracker notifications; 1801 RegisterForAllNavNotifications(¬ifications, &controller); 1802 1803 const GURL url1("http://foo1"); 1804 const GURL url2("http://foo2"); // Redirection target 1805 1806 // First request 1807 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 1808 1809 EXPECT_TRUE(controller.GetPendingEntry()); 1810 EXPECT_EQ(controller.GetPendingEntryIndex(), -1); 1811 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL()); 1812 1813 FrameHostMsg_DidCommitProvisionalLoad_Params params; 1814 params.page_id = 0; 1815 params.url = url2; 1816 params.transition = PAGE_TRANSITION_SERVER_REDIRECT; 1817 params.redirects.push_back(GURL("http://foo1")); 1818 params.redirects.push_back(GURL("http://foo2")); 1819 params.should_update_history = false; 1820 params.gesture = NavigationGestureAuto; 1821 params.is_post = false; 1822 params.page_state = PageState::CreateFromURL(url2); 1823 1824 LoadCommittedDetails details; 1825 1826 EXPECT_EQ(0U, notifications.size()); 1827 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params, 1828 &details)); 1829 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1830 navigation_entry_committed_counter_ = 0; 1831 1832 EXPECT_TRUE(details.type == NAVIGATION_TYPE_NEW_PAGE); 1833 EXPECT_EQ(controller.GetEntryCount(), 1); 1834 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0); 1835 EXPECT_TRUE(controller.GetLastCommittedEntry()); 1836 EXPECT_EQ(controller.GetPendingEntryIndex(), -1); 1837 EXPECT_FALSE(controller.GetPendingEntry()); 1838 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL()); 1839 1840 EXPECT_FALSE(controller.CanGoBack()); 1841 EXPECT_FALSE(controller.CanGoForward()); 1842 } 1843 1844 // Tests navigation via link click within a subframe. A new navigation entry 1845 // should be created. 1846 TEST_F(NavigationControllerTest, NewSubframe) { 1847 NavigationControllerImpl& controller = controller_impl(); 1848 TestNotificationTracker notifications; 1849 RegisterForAllNavNotifications(¬ifications, &controller); 1850 1851 const GURL url1("http://foo1"); 1852 main_test_rfh()->SendNavigate(0, url1); 1853 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1854 navigation_entry_committed_counter_ = 0; 1855 1856 const GURL url2("http://foo2"); 1857 FrameHostMsg_DidCommitProvisionalLoad_Params params; 1858 params.page_id = 1; 1859 params.url = url2; 1860 params.transition = PAGE_TRANSITION_MANUAL_SUBFRAME; 1861 params.should_update_history = false; 1862 params.gesture = NavigationGestureUser; 1863 params.is_post = false; 1864 params.page_state = PageState::CreateFromURL(url2); 1865 1866 LoadCommittedDetails details; 1867 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params, 1868 &details)); 1869 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1870 navigation_entry_committed_counter_ = 0; 1871 EXPECT_EQ(url1, details.previous_url); 1872 EXPECT_FALSE(details.is_in_page); 1873 EXPECT_FALSE(details.is_main_frame); 1874 1875 // The new entry should be appended. 1876 EXPECT_EQ(2, controller.GetEntryCount()); 1877 1878 // New entry should refer to the new page, but the old URL (entries only 1879 // reflect the toplevel URL). 1880 EXPECT_EQ(url1, details.entry->GetURL()); 1881 EXPECT_EQ(params.page_id, details.entry->GetPageID()); 1882 } 1883 1884 // Some pages create a popup, then write an iframe into it. This causes a 1885 // subframe navigation without having any committed entry. Such navigations 1886 // just get thrown on the ground, but we shouldn't crash. 1887 TEST_F(NavigationControllerTest, SubframeOnEmptyPage) { 1888 NavigationControllerImpl& controller = controller_impl(); 1889 TestNotificationTracker notifications; 1890 RegisterForAllNavNotifications(¬ifications, &controller); 1891 1892 // Navigation controller currently has no entries. 1893 const GURL url("http://foo2"); 1894 FrameHostMsg_DidCommitProvisionalLoad_Params params; 1895 params.page_id = 1; 1896 params.url = url; 1897 params.transition = PAGE_TRANSITION_AUTO_SUBFRAME; 1898 params.should_update_history = false; 1899 params.gesture = NavigationGestureAuto; 1900 params.is_post = false; 1901 params.page_state = PageState::CreateFromURL(url); 1902 1903 LoadCommittedDetails details; 1904 EXPECT_FALSE(controller.RendererDidNavigate(main_test_rfh(), params, 1905 &details)); 1906 EXPECT_EQ(0U, notifications.size()); 1907 } 1908 1909 // Auto subframes are ones the page loads automatically like ads. They should 1910 // not create new navigation entries. 1911 TEST_F(NavigationControllerTest, AutoSubframe) { 1912 NavigationControllerImpl& controller = controller_impl(); 1913 TestNotificationTracker notifications; 1914 RegisterForAllNavNotifications(¬ifications, &controller); 1915 1916 const GURL url1("http://foo1"); 1917 main_test_rfh()->SendNavigate(0, url1); 1918 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1919 navigation_entry_committed_counter_ = 0; 1920 1921 const GURL url2("http://foo2"); 1922 FrameHostMsg_DidCommitProvisionalLoad_Params params; 1923 params.page_id = 0; 1924 params.url = url2; 1925 params.transition = PAGE_TRANSITION_AUTO_SUBFRAME; 1926 params.should_update_history = false; 1927 params.gesture = NavigationGestureUser; 1928 params.is_post = false; 1929 params.page_state = PageState::CreateFromURL(url2); 1930 1931 // Navigating should do nothing. 1932 LoadCommittedDetails details; 1933 EXPECT_FALSE(controller.RendererDidNavigate(main_test_rfh(), params, 1934 &details)); 1935 EXPECT_EQ(0U, notifications.size()); 1936 1937 // There should still be only one entry. 1938 EXPECT_EQ(1, controller.GetEntryCount()); 1939 } 1940 1941 // Tests navigation and then going back to a subframe navigation. 1942 TEST_F(NavigationControllerTest, BackSubframe) { 1943 NavigationControllerImpl& controller = controller_impl(); 1944 TestNotificationTracker notifications; 1945 RegisterForAllNavNotifications(¬ifications, &controller); 1946 1947 // Main page. 1948 const GURL url1("http://foo1"); 1949 main_test_rfh()->SendNavigate(0, url1); 1950 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1951 navigation_entry_committed_counter_ = 0; 1952 1953 // First manual subframe navigation. 1954 const GURL url2("http://foo2"); 1955 FrameHostMsg_DidCommitProvisionalLoad_Params params; 1956 params.page_id = 1; 1957 params.url = url2; 1958 params.transition = PAGE_TRANSITION_MANUAL_SUBFRAME; 1959 params.should_update_history = false; 1960 params.gesture = NavigationGestureUser; 1961 params.is_post = false; 1962 params.page_state = PageState::CreateFromURL(url2); 1963 1964 // This should generate a new entry. 1965 LoadCommittedDetails details; 1966 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params, 1967 &details)); 1968 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1969 navigation_entry_committed_counter_ = 0; 1970 EXPECT_EQ(2, controller.GetEntryCount()); 1971 1972 // Second manual subframe navigation should also make a new entry. 1973 const GURL url3("http://foo3"); 1974 params.page_id = 2; 1975 params.url = url3; 1976 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params, 1977 &details)); 1978 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1979 navigation_entry_committed_counter_ = 0; 1980 EXPECT_EQ(3, controller.GetEntryCount()); 1981 EXPECT_EQ(2, controller.GetCurrentEntryIndex()); 1982 1983 // Go back one. 1984 controller.GoBack(); 1985 params.url = url2; 1986 params.page_id = 1; 1987 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params, 1988 &details)); 1989 EXPECT_EQ(1U, navigation_entry_committed_counter_); 1990 navigation_entry_committed_counter_ = 0; 1991 EXPECT_EQ(3, controller.GetEntryCount()); 1992 EXPECT_EQ(1, controller.GetCurrentEntryIndex()); 1993 EXPECT_EQ(-1, controller.GetPendingEntryIndex()); 1994 EXPECT_FALSE(controller.GetPendingEntry()); 1995 1996 // Go back one more. 1997 controller.GoBack(); 1998 params.url = url1; 1999 params.page_id = 0; 2000 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params, 2001 &details)); 2002 EXPECT_EQ(1U, navigation_entry_committed_counter_); 2003 navigation_entry_committed_counter_ = 0; 2004 EXPECT_EQ(3, controller.GetEntryCount()); 2005 EXPECT_EQ(0, controller.GetCurrentEntryIndex()); 2006 EXPECT_EQ(-1, controller.GetPendingEntryIndex()); 2007 EXPECT_FALSE(controller.GetPendingEntry()); 2008 } 2009 2010 TEST_F(NavigationControllerTest, LinkClick) { 2011 NavigationControllerImpl& controller = controller_impl(); 2012 TestNotificationTracker notifications; 2013 RegisterForAllNavNotifications(¬ifications, &controller); 2014 2015 const GURL url1("http://foo1"); 2016 const GURL url2("http://foo2"); 2017 2018 main_test_rfh()->SendNavigate(0, url1); 2019 EXPECT_EQ(1U, navigation_entry_committed_counter_); 2020 navigation_entry_committed_counter_ = 0; 2021 2022 main_test_rfh()->SendNavigate(1, url2); 2023 EXPECT_EQ(1U, navigation_entry_committed_counter_); 2024 navigation_entry_committed_counter_ = 0; 2025 2026 // Should not have produced a new session history entry. 2027 EXPECT_EQ(controller.GetEntryCount(), 2); 2028 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1); 2029 EXPECT_EQ(controller.GetPendingEntryIndex(), -1); 2030 EXPECT_TRUE(controller.GetLastCommittedEntry()); 2031 EXPECT_FALSE(controller.GetPendingEntry()); 2032 EXPECT_TRUE(controller.CanGoBack()); 2033 EXPECT_FALSE(controller.CanGoForward()); 2034 } 2035 2036 TEST_F(NavigationControllerTest, InPage) { 2037 NavigationControllerImpl& controller = controller_impl(); 2038 TestNotificationTracker notifications; 2039 RegisterForAllNavNotifications(¬ifications, &controller); 2040 2041 // Main page. 2042 const GURL url1("http://foo"); 2043 main_test_rfh()->SendNavigate(0, url1); 2044 EXPECT_EQ(1U, navigation_entry_committed_counter_); 2045 navigation_entry_committed_counter_ = 0; 2046 2047 // Ensure main page navigation to same url respects the was_within_same_page 2048 // hint provided in the params. 2049 FrameHostMsg_DidCommitProvisionalLoad_Params self_params; 2050 self_params.page_id = 0; 2051 self_params.url = url1; 2052 self_params.transition = PAGE_TRANSITION_LINK; 2053 self_params.should_update_history = false; 2054 self_params.gesture = NavigationGestureUser; 2055 self_params.is_post = false; 2056 self_params.page_state = PageState::CreateFromURL(url1); 2057 self_params.was_within_same_page = true; 2058 2059 LoadCommittedDetails details; 2060 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), self_params, 2061 &details)); 2062 EXPECT_EQ(1U, navigation_entry_committed_counter_); 2063 navigation_entry_committed_counter_ = 0; 2064 EXPECT_TRUE(details.is_in_page); 2065 EXPECT_TRUE(details.did_replace_entry); 2066 EXPECT_EQ(1, controller.GetEntryCount()); 2067 2068 // Fragment navigation to a new page_id. 2069 const GURL url2("http://foo#a"); 2070 FrameHostMsg_DidCommitProvisionalLoad_Params params; 2071 params.page_id = 1; 2072 params.url = url2; 2073 params.transition = PAGE_TRANSITION_LINK; 2074 params.should_update_history = false; 2075 params.gesture = NavigationGestureUser; 2076 params.is_post = false; 2077 params.page_state = PageState::CreateFromURL(url2); 2078 params.was_within_same_page = true; 2079 2080 // This should generate a new entry. 2081 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params, 2082 &details)); 2083 EXPECT_EQ(1U, navigation_entry_committed_counter_); 2084 navigation_entry_committed_counter_ = 0; 2085 EXPECT_TRUE(details.is_in_page); 2086 EXPECT_FALSE(details.did_replace_entry); 2087 EXPECT_EQ(2, controller.GetEntryCount()); 2088 2089 // Go back one. 2090 FrameHostMsg_DidCommitProvisionalLoad_Params back_params(params); 2091 controller.GoBack(); 2092 back_params.url = url1; 2093 back_params.page_id = 0; 2094 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), back_params, 2095 &details)); 2096 EXPECT_EQ(1U, navigation_entry_committed_counter_); 2097 navigation_entry_committed_counter_ = 0; 2098 EXPECT_TRUE(details.is_in_page); 2099 EXPECT_EQ(2, controller.GetEntryCount()); 2100 EXPECT_EQ(0, controller.GetCurrentEntryIndex()); 2101 EXPECT_EQ(back_params.url, controller.GetVisibleEntry()->GetURL()); 2102 2103 // Go forward 2104 FrameHostMsg_DidCommitProvisionalLoad_Params forward_params(params); 2105 controller.GoForward(); 2106 forward_params.url = url2; 2107 forward_params.page_id = 1; 2108 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), forward_params, 2109 &details)); 2110 EXPECT_EQ(1U, navigation_entry_committed_counter_); 2111 navigation_entry_committed_counter_ = 0; 2112 EXPECT_TRUE(details.is_in_page); 2113 EXPECT_EQ(2, controller.GetEntryCount()); 2114 EXPECT_EQ(1, controller.GetCurrentEntryIndex()); 2115 EXPECT_EQ(forward_params.url, 2116 controller.GetVisibleEntry()->GetURL()); 2117 2118 // Now go back and forward again. This is to work around a bug where we would 2119 // compare the incoming URL with the last committed entry rather than the 2120 // one identified by an existing page ID. This would result in the second URL 2121 // losing the reference fragment when you navigate away from it and then back. 2122 controller.GoBack(); 2123 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), back_params, 2124 &details)); 2125 controller.GoForward(); 2126 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), forward_params, 2127 &details)); 2128 EXPECT_EQ(forward_params.url, 2129 controller.GetVisibleEntry()->GetURL()); 2130 2131 // Finally, navigate to an unrelated URL to make sure in_page is not sticky. 2132 const GURL url3("http://bar"); 2133 params.page_id = 2; 2134 params.url = url3; 2135 navigation_entry_committed_counter_ = 0; 2136 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params, 2137 &details)); 2138 EXPECT_EQ(1U, navigation_entry_committed_counter_); 2139 navigation_entry_committed_counter_ = 0; 2140 EXPECT_FALSE(details.is_in_page); 2141 EXPECT_EQ(3, controller.GetEntryCount()); 2142 EXPECT_EQ(2, controller.GetCurrentEntryIndex()); 2143 } 2144 2145 TEST_F(NavigationControllerTest, InPage_Replace) { 2146 NavigationControllerImpl& controller = controller_impl(); 2147 TestNotificationTracker notifications; 2148 RegisterForAllNavNotifications(¬ifications, &controller); 2149 2150 // Main page. 2151 const GURL url1("http://foo"); 2152 main_test_rfh()->SendNavigate(0, url1); 2153 EXPECT_EQ(1U, navigation_entry_committed_counter_); 2154 navigation_entry_committed_counter_ = 0; 2155 2156 // First navigation. 2157 const GURL url2("http://foo#a"); 2158 FrameHostMsg_DidCommitProvisionalLoad_Params params; 2159 params.page_id = 0; // Same page_id 2160 params.url = url2; 2161 params.transition = PAGE_TRANSITION_LINK; 2162 params.should_update_history = false; 2163 params.gesture = NavigationGestureUser; 2164 params.is_post = false; 2165 params.page_state = PageState::CreateFromURL(url2); 2166 params.was_within_same_page = true; 2167 2168 // This should NOT generate a new entry, nor prune the list. 2169 LoadCommittedDetails details; 2170 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params, 2171 &details)); 2172 EXPECT_EQ(1U, navigation_entry_committed_counter_); 2173 navigation_entry_committed_counter_ = 0; 2174 EXPECT_TRUE(details.is_in_page); 2175 EXPECT_TRUE(details.did_replace_entry); 2176 EXPECT_EQ(1, controller.GetEntryCount()); 2177 } 2178 2179 // Tests for http://crbug.com/40395 2180 // Simulates this: 2181 // <script> 2182 // window.location.replace("#a"); 2183 // window.location='http://foo3/'; 2184 // </script> 2185 TEST_F(NavigationControllerTest, ClientRedirectAfterInPageNavigation) { 2186 NavigationControllerImpl& controller = controller_impl(); 2187 TestNotificationTracker notifications; 2188 RegisterForAllNavNotifications(¬ifications, &controller); 2189 2190 // Load an initial page. 2191 { 2192 const GURL url("http://foo/"); 2193 main_test_rfh()->SendNavigate(0, url); 2194 EXPECT_EQ(1U, navigation_entry_committed_counter_); 2195 navigation_entry_committed_counter_ = 0; 2196 } 2197 2198 // Navigate to a new page. 2199 { 2200 const GURL url("http://foo2/"); 2201 main_test_rfh()->SendNavigate(1, url); 2202 EXPECT_EQ(1U, navigation_entry_committed_counter_); 2203 navigation_entry_committed_counter_ = 0; 2204 } 2205 2206 // Navigate within the page. 2207 { 2208 const GURL url("http://foo2/#a"); 2209 FrameHostMsg_DidCommitProvisionalLoad_Params params; 2210 params.page_id = 1; // Same page_id 2211 params.url = url; 2212 params.transition = PAGE_TRANSITION_LINK; 2213 params.redirects.push_back(url); 2214 params.should_update_history = true; 2215 params.gesture = NavigationGestureUnknown; 2216 params.is_post = false; 2217 params.page_state = PageState::CreateFromURL(url); 2218 params.was_within_same_page = true; 2219 2220 // This should NOT generate a new entry, nor prune the list. 2221 LoadCommittedDetails details; 2222 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params, 2223 &details)); 2224 EXPECT_EQ(1U, navigation_entry_committed_counter_); 2225 navigation_entry_committed_counter_ = 0; 2226 EXPECT_TRUE(details.is_in_page); 2227 EXPECT_TRUE(details.did_replace_entry); 2228 EXPECT_EQ(2, controller.GetEntryCount()); 2229 } 2230 2231 // Perform a client redirect to a new page. 2232 { 2233 const GURL url("http://foo3/"); 2234 FrameHostMsg_DidCommitProvisionalLoad_Params params; 2235 params.page_id = 2; // New page_id 2236 params.url = url; 2237 params.transition = PAGE_TRANSITION_CLIENT_REDIRECT; 2238 params.redirects.push_back(GURL("http://foo2/#a")); 2239 params.redirects.push_back(url); 2240 params.should_update_history = true; 2241 params.gesture = NavigationGestureUnknown; 2242 params.is_post = false; 2243 params.page_state = PageState::CreateFromURL(url); 2244 2245 // This SHOULD generate a new entry. 2246 LoadCommittedDetails details; 2247 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params, 2248 &details)); 2249 EXPECT_EQ(1U, navigation_entry_committed_counter_); 2250 navigation_entry_committed_counter_ = 0; 2251 EXPECT_FALSE(details.is_in_page); 2252 EXPECT_EQ(3, controller.GetEntryCount()); 2253 } 2254 2255 // Verify that BACK brings us back to http://foo2/. 2256 { 2257 const GURL url("http://foo2/"); 2258 controller.GoBack(); 2259 main_test_rfh()->SendNavigate(1, url); 2260 EXPECT_EQ(1U, navigation_entry_committed_counter_); 2261 navigation_entry_committed_counter_ = 0; 2262 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL()); 2263 } 2264 } 2265 2266 TEST_F(NavigationControllerTest, PushStateWithoutPreviousEntry) 2267 { 2268 ASSERT_FALSE(controller_impl().GetLastCommittedEntry()); 2269 FrameHostMsg_DidCommitProvisionalLoad_Params params; 2270 GURL url("http://foo"); 2271 params.page_id = 1; 2272 params.url = url; 2273 params.page_state = PageState::CreateFromURL(url); 2274 params.was_within_same_page = true; 2275 test_rvh()->SendNavigateWithParams(¶ms); 2276 // We pass if we don't crash. 2277 } 2278 2279 // NotificationObserver implementation used in verifying we've received the 2280 // NOTIFICATION_NAV_LIST_PRUNED method. 2281 class PrunedListener : public NotificationObserver { 2282 public: 2283 explicit PrunedListener(NavigationControllerImpl* controller) 2284 : notification_count_(0) { 2285 registrar_.Add(this, NOTIFICATION_NAV_LIST_PRUNED, 2286 Source<NavigationController>(controller)); 2287 } 2288 2289 virtual void Observe(int type, 2290 const NotificationSource& source, 2291 const NotificationDetails& details) OVERRIDE { 2292 if (type == NOTIFICATION_NAV_LIST_PRUNED) { 2293 notification_count_++; 2294 details_ = *(Details<PrunedDetails>(details).ptr()); 2295 } 2296 } 2297 2298 // Number of times NAV_LIST_PRUNED has been observed. 2299 int notification_count_; 2300 2301 // Details from the last NAV_LIST_PRUNED. 2302 PrunedDetails details_; 2303 2304 private: 2305 NotificationRegistrar registrar_; 2306 2307 DISALLOW_COPY_AND_ASSIGN(PrunedListener); 2308 }; 2309 2310 // Tests that we limit the number of navigation entries created correctly. 2311 TEST_F(NavigationControllerTest, EnforceMaxNavigationCount) { 2312 NavigationControllerImpl& controller = controller_impl(); 2313 size_t original_count = NavigationControllerImpl::max_entry_count(); 2314 const int kMaxEntryCount = 5; 2315 2316 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount); 2317 2318 int url_index; 2319 // Load up to the max count, all entries should be there. 2320 for (url_index = 0; url_index < kMaxEntryCount; url_index++) { 2321 GURL url(base::StringPrintf("http://www.a.com/%d", url_index)); 2322 controller.LoadURL( 2323 url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 2324 main_test_rfh()->SendNavigate(url_index, url); 2325 } 2326 2327 EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount); 2328 2329 // Created a PrunedListener to observe prune notifications. 2330 PrunedListener listener(&controller); 2331 2332 // Navigate some more. 2333 GURL url(base::StringPrintf("http://www.a.com/%d", url_index)); 2334 controller.LoadURL( 2335 url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 2336 main_test_rfh()->SendNavigate(url_index, url); 2337 url_index++; 2338 2339 // We should have got a pruned navigation. 2340 EXPECT_EQ(1, listener.notification_count_); 2341 EXPECT_TRUE(listener.details_.from_front); 2342 EXPECT_EQ(1, listener.details_.count); 2343 2344 // We expect http://www.a.com/0 to be gone. 2345 EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount); 2346 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), 2347 GURL("http:////www.a.com/1")); 2348 2349 // More navigations. 2350 for (int i = 0; i < 3; i++) { 2351 url = GURL(base::StringPrintf("http:////www.a.com/%d", url_index)); 2352 controller.LoadURL( 2353 url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 2354 main_test_rfh()->SendNavigate(url_index, url); 2355 url_index++; 2356 } 2357 EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount); 2358 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), 2359 GURL("http:////www.a.com/4")); 2360 2361 NavigationControllerImpl::set_max_entry_count_for_testing(original_count); 2362 } 2363 2364 // Tests that we can do a restore and navigate to the restored entries and 2365 // everything is updated properly. This can be tricky since there is no 2366 // SiteInstance for the entries created initially. 2367 TEST_F(NavigationControllerTest, RestoreNavigate) { 2368 // Create a NavigationController with a restored set of tabs. 2369 GURL url("http://foo"); 2370 std::vector<NavigationEntry*> entries; 2371 NavigationEntry* entry = NavigationControllerImpl::CreateNavigationEntry( 2372 url, Referrer(), PAGE_TRANSITION_RELOAD, false, std::string(), 2373 browser_context()); 2374 entry->SetPageID(0); 2375 entry->SetTitle(base::ASCIIToUTF16("Title")); 2376 entry->SetPageState(PageState::CreateFromEncodedData("state")); 2377 const base::Time timestamp = base::Time::Now(); 2378 entry->SetTimestamp(timestamp); 2379 entries.push_back(entry); 2380 scoped_ptr<WebContentsImpl> our_contents(static_cast<WebContentsImpl*>( 2381 WebContents::Create(WebContents::CreateParams(browser_context())))); 2382 NavigationControllerImpl& our_controller = our_contents->GetController(); 2383 our_controller.Restore( 2384 0, 2385 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY, 2386 &entries); 2387 ASSERT_EQ(0u, entries.size()); 2388 2389 // Before navigating to the restored entry, it should have a restore_type 2390 // and no SiteInstance. 2391 ASSERT_EQ(1, our_controller.GetEntryCount()); 2392 EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY, 2393 NavigationEntryImpl::FromNavigationEntry( 2394 our_controller.GetEntryAtIndex(0))->restore_type()); 2395 EXPECT_FALSE(NavigationEntryImpl::FromNavigationEntry( 2396 our_controller.GetEntryAtIndex(0))->site_instance()); 2397 2398 // After navigating, we should have one entry, and it should be "pending". 2399 // It should now have a SiteInstance and no restore_type. 2400 our_controller.GoToIndex(0); 2401 EXPECT_EQ(1, our_controller.GetEntryCount()); 2402 EXPECT_EQ(our_controller.GetEntryAtIndex(0), 2403 our_controller.GetPendingEntry()); 2404 EXPECT_EQ(0, our_controller.GetEntryAtIndex(0)->GetPageID()); 2405 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE, 2406 NavigationEntryImpl::FromNavigationEntry 2407 (our_controller.GetEntryAtIndex(0))->restore_type()); 2408 EXPECT_TRUE(NavigationEntryImpl::FromNavigationEntry( 2409 our_controller.GetEntryAtIndex(0))->site_instance()); 2410 2411 // Timestamp should remain the same before the navigation finishes. 2412 EXPECT_EQ(timestamp, our_controller.GetEntryAtIndex(0)->GetTimestamp()); 2413 2414 // Say we navigated to that entry. 2415 FrameHostMsg_DidCommitProvisionalLoad_Params params; 2416 params.page_id = 0; 2417 params.url = url; 2418 params.transition = PAGE_TRANSITION_LINK; 2419 params.should_update_history = false; 2420 params.gesture = NavigationGestureUser; 2421 params.is_post = false; 2422 params.page_state = PageState::CreateFromURL(url); 2423 LoadCommittedDetails details; 2424 our_controller.RendererDidNavigate(our_contents->GetMainFrame(), params, 2425 &details); 2426 2427 // There should be no longer any pending entry and one committed one. This 2428 // means that we were able to locate the entry, assign its site instance, and 2429 // commit it properly. 2430 EXPECT_EQ(1, our_controller.GetEntryCount()); 2431 EXPECT_EQ(0, our_controller.GetLastCommittedEntryIndex()); 2432 EXPECT_FALSE(our_controller.GetPendingEntry()); 2433 EXPECT_EQ(url, 2434 NavigationEntryImpl::FromNavigationEntry( 2435 our_controller.GetLastCommittedEntry())->site_instance()-> 2436 GetSiteURL()); 2437 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE, 2438 NavigationEntryImpl::FromNavigationEntry( 2439 our_controller.GetEntryAtIndex(0))->restore_type()); 2440 2441 // Timestamp should have been updated. 2442 EXPECT_GE(our_controller.GetEntryAtIndex(0)->GetTimestamp(), timestamp); 2443 } 2444 2445 // Tests that we can still navigate to a restored entry after a different 2446 // navigation fails and clears the pending entry. http://crbug.com/90085 2447 TEST_F(NavigationControllerTest, RestoreNavigateAfterFailure) { 2448 // Create a NavigationController with a restored set of tabs. 2449 GURL url("http://foo"); 2450 std::vector<NavigationEntry*> entries; 2451 NavigationEntry* entry = NavigationControllerImpl::CreateNavigationEntry( 2452 url, Referrer(), PAGE_TRANSITION_RELOAD, false, std::string(), 2453 browser_context()); 2454 entry->SetPageID(0); 2455 entry->SetTitle(base::ASCIIToUTF16("Title")); 2456 entry->SetPageState(PageState::CreateFromEncodedData("state")); 2457 entries.push_back(entry); 2458 scoped_ptr<WebContentsImpl> our_contents(static_cast<WebContentsImpl*>( 2459 WebContents::Create(WebContents::CreateParams(browser_context())))); 2460 NavigationControllerImpl& our_controller = our_contents->GetController(); 2461 our_controller.Restore( 2462 0, NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY, &entries); 2463 ASSERT_EQ(0u, entries.size()); 2464 2465 // Before navigating to the restored entry, it should have a restore_type 2466 // and no SiteInstance. 2467 EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY, 2468 NavigationEntryImpl::FromNavigationEntry( 2469 our_controller.GetEntryAtIndex(0))->restore_type()); 2470 EXPECT_FALSE(NavigationEntryImpl::FromNavigationEntry( 2471 our_controller.GetEntryAtIndex(0))->site_instance()); 2472 2473 // After navigating, we should have one entry, and it should be "pending". 2474 // It should now have a SiteInstance and no restore_type. 2475 our_controller.GoToIndex(0); 2476 EXPECT_EQ(1, our_controller.GetEntryCount()); 2477 EXPECT_EQ(our_controller.GetEntryAtIndex(0), 2478 our_controller.GetPendingEntry()); 2479 EXPECT_EQ(0, our_controller.GetEntryAtIndex(0)->GetPageID()); 2480 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE, 2481 NavigationEntryImpl::FromNavigationEntry( 2482 our_controller.GetEntryAtIndex(0))->restore_type()); 2483 EXPECT_TRUE(NavigationEntryImpl::FromNavigationEntry( 2484 our_controller.GetEntryAtIndex(0))->site_instance()); 2485 2486 // This pending navigation may have caused a different navigation to fail, 2487 // which causes the pending entry to be cleared. 2488 FrameHostMsg_DidFailProvisionalLoadWithError_Params fail_load_params; 2489 fail_load_params.error_code = net::ERR_ABORTED; 2490 fail_load_params.error_description = base::string16(); 2491 fail_load_params.url = url; 2492 fail_load_params.showing_repost_interstitial = false; 2493 main_test_rfh()->OnMessageReceived( 2494 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id 2495 fail_load_params)); 2496 2497 // Now the pending restored entry commits. 2498 FrameHostMsg_DidCommitProvisionalLoad_Params params; 2499 params.page_id = 0; 2500 params.url = url; 2501 params.transition = PAGE_TRANSITION_LINK; 2502 params.should_update_history = false; 2503 params.gesture = NavigationGestureUser; 2504 params.is_post = false; 2505 params.page_state = PageState::CreateFromURL(url); 2506 LoadCommittedDetails details; 2507 our_controller.RendererDidNavigate(our_contents->GetMainFrame(), params, 2508 &details); 2509 2510 // There should be no pending entry and one committed one. 2511 EXPECT_EQ(1, our_controller.GetEntryCount()); 2512 EXPECT_EQ(0, our_controller.GetLastCommittedEntryIndex()); 2513 EXPECT_FALSE(our_controller.GetPendingEntry()); 2514 EXPECT_EQ(url, 2515 NavigationEntryImpl::FromNavigationEntry( 2516 our_controller.GetLastCommittedEntry())->site_instance()-> 2517 GetSiteURL()); 2518 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE, 2519 NavigationEntryImpl::FromNavigationEntry( 2520 our_controller.GetEntryAtIndex(0))->restore_type()); 2521 } 2522 2523 // Make sure that the page type and stuff is correct after an interstitial. 2524 TEST_F(NavigationControllerTest, Interstitial) { 2525 NavigationControllerImpl& controller = controller_impl(); 2526 // First navigate somewhere normal. 2527 const GURL url1("http://foo"); 2528 controller.LoadURL( 2529 url1, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 2530 main_test_rfh()->SendNavigate(0, url1); 2531 2532 // Now navigate somewhere with an interstitial. 2533 const GURL url2("http://bar"); 2534 controller.LoadURL( 2535 url1, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 2536 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())-> 2537 set_page_type(PAGE_TYPE_INTERSTITIAL); 2538 2539 // At this point the interstitial will be displayed and the load will still 2540 // be pending. If the user continues, the load will commit. 2541 main_test_rfh()->SendNavigate(1, url2); 2542 2543 // The page should be a normal page again. 2544 EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetURL()); 2545 EXPECT_EQ(PAGE_TYPE_NORMAL, 2546 controller.GetLastCommittedEntry()->GetPageType()); 2547 } 2548 2549 TEST_F(NavigationControllerTest, RemoveEntry) { 2550 NavigationControllerImpl& controller = controller_impl(); 2551 const GURL url1("http://foo/1"); 2552 const GURL url2("http://foo/2"); 2553 const GURL url3("http://foo/3"); 2554 const GURL url4("http://foo/4"); 2555 const GURL url5("http://foo/5"); 2556 const GURL pending_url("http://foo/pending"); 2557 const GURL default_url("http://foo/default"); 2558 2559 controller.LoadURL( 2560 url1, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 2561 main_test_rfh()->SendNavigate(0, url1); 2562 controller.LoadURL( 2563 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 2564 main_test_rfh()->SendNavigate(1, url2); 2565 controller.LoadURL( 2566 url3, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 2567 main_test_rfh()->SendNavigate(2, url3); 2568 controller.LoadURL( 2569 url4, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 2570 main_test_rfh()->SendNavigate(3, url4); 2571 controller.LoadURL( 2572 url5, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 2573 main_test_rfh()->SendNavigate(4, url5); 2574 2575 // Try to remove the last entry. Will fail because it is the current entry. 2576 EXPECT_FALSE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 1)); 2577 EXPECT_EQ(5, controller.GetEntryCount()); 2578 EXPECT_EQ(4, controller.GetLastCommittedEntryIndex()); 2579 2580 // Go back, but don't commit yet. Check that we can't delete the current 2581 // and pending entries. 2582 controller.GoBack(); 2583 EXPECT_FALSE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 1)); 2584 EXPECT_FALSE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 2)); 2585 2586 // Now commit and delete the last entry. 2587 main_test_rfh()->SendNavigate(3, url4); 2588 EXPECT_TRUE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 1)); 2589 EXPECT_EQ(4, controller.GetEntryCount()); 2590 EXPECT_EQ(3, controller.GetLastCommittedEntryIndex()); 2591 EXPECT_FALSE(controller.GetPendingEntry()); 2592 2593 // Remove an entry which is not the last committed one. 2594 EXPECT_TRUE(controller.RemoveEntryAtIndex(0)); 2595 EXPECT_EQ(3, controller.GetEntryCount()); 2596 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex()); 2597 EXPECT_FALSE(controller.GetPendingEntry()); 2598 2599 // Remove the 2 remaining entries. 2600 controller.RemoveEntryAtIndex(1); 2601 controller.RemoveEntryAtIndex(0); 2602 2603 // This should leave us with only the last committed entry. 2604 EXPECT_EQ(1, controller.GetEntryCount()); 2605 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex()); 2606 } 2607 2608 // Tests the transient entry, making sure it goes away with all navigations. 2609 TEST_F(NavigationControllerTest, TransientEntry) { 2610 NavigationControllerImpl& controller = controller_impl(); 2611 TestNotificationTracker notifications; 2612 RegisterForAllNavNotifications(¬ifications, &controller); 2613 2614 const GURL url0("http://foo/0"); 2615 const GURL url1("http://foo/1"); 2616 const GURL url2("http://foo/2"); 2617 const GURL url3("http://foo/3"); 2618 const GURL url3_ref("http://foo/3#bar"); 2619 const GURL url4("http://foo/4"); 2620 const GURL transient_url("http://foo/transient"); 2621 2622 controller.LoadURL( 2623 url0, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 2624 main_test_rfh()->SendNavigate(0, url0); 2625 controller.LoadURL( 2626 url1, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 2627 main_test_rfh()->SendNavigate(1, url1); 2628 2629 notifications.Reset(); 2630 2631 // Adding a transient with no pending entry. 2632 NavigationEntryImpl* transient_entry = new NavigationEntryImpl; 2633 transient_entry->SetURL(transient_url); 2634 controller.SetTransientEntry(transient_entry); 2635 2636 // We should not have received any notifications. 2637 EXPECT_EQ(0U, notifications.size()); 2638 2639 // Check our state. 2640 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL()); 2641 EXPECT_EQ(controller.GetEntryCount(), 3); 2642 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1); 2643 EXPECT_EQ(controller.GetPendingEntryIndex(), -1); 2644 EXPECT_TRUE(controller.GetLastCommittedEntry()); 2645 EXPECT_FALSE(controller.GetPendingEntry()); 2646 EXPECT_TRUE(controller.CanGoBack()); 2647 EXPECT_FALSE(controller.CanGoForward()); 2648 EXPECT_EQ(contents()->GetMaxPageID(), 1); 2649 2650 // Navigate. 2651 controller.LoadURL( 2652 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 2653 main_test_rfh()->SendNavigate(2, url2); 2654 2655 // We should have navigated, transient entry should be gone. 2656 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL()); 2657 EXPECT_EQ(controller.GetEntryCount(), 3); 2658 2659 // Add a transient again, then navigate with no pending entry this time. 2660 transient_entry = new NavigationEntryImpl; 2661 transient_entry->SetURL(transient_url); 2662 controller.SetTransientEntry(transient_entry); 2663 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL()); 2664 main_test_rfh()->SendNavigate(3, url3); 2665 // Transient entry should be gone. 2666 EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL()); 2667 EXPECT_EQ(controller.GetEntryCount(), 4); 2668 2669 // Initiate a navigation, add a transient then commit navigation. 2670 controller.LoadURL( 2671 url4, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 2672 transient_entry = new NavigationEntryImpl; 2673 transient_entry->SetURL(transient_url); 2674 controller.SetTransientEntry(transient_entry); 2675 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL()); 2676 main_test_rfh()->SendNavigate(4, url4); 2677 EXPECT_EQ(url4, controller.GetVisibleEntry()->GetURL()); 2678 EXPECT_EQ(controller.GetEntryCount(), 5); 2679 2680 // Add a transient and go back. This should simply remove the transient. 2681 transient_entry = new NavigationEntryImpl; 2682 transient_entry->SetURL(transient_url); 2683 controller.SetTransientEntry(transient_entry); 2684 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL()); 2685 EXPECT_TRUE(controller.CanGoBack()); 2686 EXPECT_FALSE(controller.CanGoForward()); 2687 controller.GoBack(); 2688 // Transient entry should be gone. 2689 EXPECT_EQ(url4, controller.GetVisibleEntry()->GetURL()); 2690 EXPECT_EQ(controller.GetEntryCount(), 5); 2691 main_test_rfh()->SendNavigate(3, url3); 2692 2693 // Add a transient and go to an entry before the current one. 2694 transient_entry = new NavigationEntryImpl; 2695 transient_entry->SetURL(transient_url); 2696 controller.SetTransientEntry(transient_entry); 2697 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL()); 2698 controller.GoToIndex(1); 2699 // The navigation should have been initiated, transient entry should be gone. 2700 EXPECT_FALSE(controller.GetTransientEntry()); 2701 EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL()); 2702 // Visible entry does not update for history navigations until commit. 2703 EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL()); 2704 main_test_rfh()->SendNavigate(1, url1); 2705 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL()); 2706 2707 // Add a transient and go to an entry after the current one. 2708 transient_entry = new NavigationEntryImpl; 2709 transient_entry->SetURL(transient_url); 2710 controller.SetTransientEntry(transient_entry); 2711 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL()); 2712 controller.GoToIndex(3); 2713 // The navigation should have been initiated, transient entry should be gone. 2714 // Because of the transient entry that is removed, going to index 3 makes us 2715 // land on url2 (which is visible after the commit). 2716 EXPECT_EQ(url2, controller.GetPendingEntry()->GetURL()); 2717 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL()); 2718 main_test_rfh()->SendNavigate(2, url2); 2719 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL()); 2720 2721 // Add a transient and go forward. 2722 transient_entry = new NavigationEntryImpl; 2723 transient_entry->SetURL(transient_url); 2724 controller.SetTransientEntry(transient_entry); 2725 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL()); 2726 EXPECT_TRUE(controller.CanGoForward()); 2727 controller.GoForward(); 2728 // We should have navigated, transient entry should be gone. 2729 EXPECT_FALSE(controller.GetTransientEntry()); 2730 EXPECT_EQ(url3, controller.GetPendingEntry()->GetURL()); 2731 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL()); 2732 main_test_rfh()->SendNavigate(3, url3); 2733 EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL()); 2734 2735 // Add a transient and do an in-page navigation, replacing the current entry. 2736 transient_entry = new NavigationEntryImpl; 2737 transient_entry->SetURL(transient_url); 2738 controller.SetTransientEntry(transient_entry); 2739 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL()); 2740 main_test_rfh()->SendNavigate(3, url3_ref); 2741 // Transient entry should be gone. 2742 EXPECT_FALSE(controller.GetTransientEntry()); 2743 EXPECT_EQ(url3_ref, controller.GetVisibleEntry()->GetURL()); 2744 2745 // Ensure the URLs are correct. 2746 EXPECT_EQ(controller.GetEntryCount(), 5); 2747 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url0); 2748 EXPECT_EQ(controller.GetEntryAtIndex(1)->GetURL(), url1); 2749 EXPECT_EQ(controller.GetEntryAtIndex(2)->GetURL(), url2); 2750 EXPECT_EQ(controller.GetEntryAtIndex(3)->GetURL(), url3_ref); 2751 EXPECT_EQ(controller.GetEntryAtIndex(4)->GetURL(), url4); 2752 } 2753 2754 // Test that Reload initiates a new navigation to a transient entry's URL. 2755 TEST_F(NavigationControllerTest, ReloadTransient) { 2756 NavigationControllerImpl& controller = controller_impl(); 2757 const GURL url0("http://foo/0"); 2758 const GURL url1("http://foo/1"); 2759 const GURL transient_url("http://foo/transient"); 2760 2761 // Load |url0|, and start a pending navigation to |url1|. 2762 controller.LoadURL( 2763 url0, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 2764 main_test_rfh()->SendNavigate(0, url0); 2765 controller.LoadURL( 2766 url1, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 2767 2768 // A transient entry is added, interrupting the navigation. 2769 NavigationEntryImpl* transient_entry = new NavigationEntryImpl; 2770 transient_entry->SetURL(transient_url); 2771 controller.SetTransientEntry(transient_entry); 2772 EXPECT_TRUE(controller.GetTransientEntry()); 2773 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL()); 2774 2775 // The page is reloaded, which should remove the pending entry for |url1| and 2776 // the transient entry for |transient_url|, and start a navigation to 2777 // |transient_url|. 2778 controller.Reload(true); 2779 EXPECT_FALSE(controller.GetTransientEntry()); 2780 EXPECT_TRUE(controller.GetPendingEntry()); 2781 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL()); 2782 ASSERT_EQ(controller.GetEntryCount(), 1); 2783 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url0); 2784 2785 // Load of |transient_url| completes. 2786 main_test_rfh()->SendNavigate(1, transient_url); 2787 ASSERT_EQ(controller.GetEntryCount(), 2); 2788 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url0); 2789 EXPECT_EQ(controller.GetEntryAtIndex(1)->GetURL(), transient_url); 2790 } 2791 2792 // Ensure that renderer initiated pending entries get replaced, so that we 2793 // don't show a stale virtual URL when a navigation commits. 2794 // See http://crbug.com/266922. 2795 TEST_F(NavigationControllerTest, RendererInitiatedPendingEntries) { 2796 NavigationControllerImpl& controller = controller_impl(); 2797 Navigator* navigator = 2798 contents()->GetFrameTree()->root()->navigator(); 2799 2800 const GURL url1("nonexistent:12121"); 2801 const GURL url1_fixed("http://nonexistent:12121/"); 2802 const GURL url2("http://foo"); 2803 2804 // We create pending entries for renderer-initiated navigations so that we 2805 // can show them in new tabs when it is safe. 2806 navigator->DidStartProvisionalLoad(main_test_rfh(), -1, url1); 2807 2808 // Simulate what happens if a BrowserURLHandler rewrites the URL, causing 2809 // the virtual URL to differ from the URL. 2810 controller.GetPendingEntry()->SetURL(url1_fixed); 2811 controller.GetPendingEntry()->SetVirtualURL(url1); 2812 2813 EXPECT_EQ(url1_fixed, controller.GetPendingEntry()->GetURL()); 2814 EXPECT_EQ(url1, controller.GetPendingEntry()->GetVirtualURL()); 2815 EXPECT_TRUE( 2816 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())-> 2817 is_renderer_initiated()); 2818 2819 // If the user clicks another link, we should replace the pending entry. 2820 navigator->DidStartProvisionalLoad(main_test_rfh(), -1, url2); 2821 EXPECT_EQ(url2, controller.GetPendingEntry()->GetURL()); 2822 EXPECT_EQ(url2, controller.GetPendingEntry()->GetVirtualURL()); 2823 2824 // Once it commits, the URL and virtual URL should reflect the actual page. 2825 main_test_rfh()->SendNavigate(0, url2); 2826 EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetURL()); 2827 EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetVirtualURL()); 2828 2829 // We should not replace the pending entry for an error URL. 2830 navigator->DidStartProvisionalLoad(main_test_rfh(), -1, url1); 2831 EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL()); 2832 navigator->DidStartProvisionalLoad( 2833 main_test_rfh(), -1, GURL(kUnreachableWebDataURL)); 2834 EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL()); 2835 2836 // We should remember if the pending entry will replace the current one. 2837 // http://crbug.com/308444. 2838 navigator->DidStartProvisionalLoad(main_test_rfh(), -1, url1); 2839 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())-> 2840 set_should_replace_entry(true); 2841 navigator->DidStartProvisionalLoad(main_test_rfh(), -1, url2); 2842 EXPECT_TRUE( 2843 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())-> 2844 should_replace_entry()); 2845 // TODO(nasko): Until OnNavigate is moved to RenderFrameHost, we need 2846 // to go through the RenderViewHost. The TestRenderViewHost routes navigations 2847 // to the main frame. 2848 main_test_rfh()->SendNavigate(0, url2); 2849 EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetURL()); 2850 } 2851 2852 // Tests that the URLs for renderer-initiated navigations are not displayed to 2853 // the user until the navigation commits, to prevent URL spoof attacks. 2854 // See http://crbug.com/99016. 2855 TEST_F(NavigationControllerTest, DontShowRendererURLUntilCommit) { 2856 NavigationControllerImpl& controller = controller_impl(); 2857 TestNotificationTracker notifications; 2858 RegisterForAllNavNotifications(¬ifications, &controller); 2859 2860 const GURL url0("http://foo/0"); 2861 const GURL url1("http://foo/1"); 2862 2863 // For typed navigations (browser-initiated), both pending and visible entries 2864 // should update before commit. 2865 controller.LoadURL(url0, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 2866 EXPECT_EQ(url0, controller.GetPendingEntry()->GetURL()); 2867 EXPECT_EQ(url0, controller.GetVisibleEntry()->GetURL()); 2868 main_test_rfh()->SendNavigate(0, url0); 2869 2870 // For link clicks (renderer-initiated navigations), the pending entry should 2871 // update before commit but the visible should not. 2872 NavigationController::LoadURLParams load_url_params(url1); 2873 load_url_params.is_renderer_initiated = true; 2874 controller.LoadURLWithParams(load_url_params); 2875 EXPECT_EQ(url0, controller.GetVisibleEntry()->GetURL()); 2876 EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL()); 2877 EXPECT_TRUE( 2878 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())-> 2879 is_renderer_initiated()); 2880 2881 // After commit, both visible should be updated, there should be no pending 2882 // entry, and we should no longer treat the entry as renderer-initiated. 2883 main_test_rfh()->SendNavigate(1, url1); 2884 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL()); 2885 EXPECT_FALSE(controller.GetPendingEntry()); 2886 EXPECT_FALSE( 2887 NavigationEntryImpl::FromNavigationEntry( 2888 controller.GetLastCommittedEntry())->is_renderer_initiated()); 2889 2890 notifications.Reset(); 2891 } 2892 2893 // Tests that the URLs for renderer-initiated navigations in new tabs are 2894 // displayed to the user before commit, as long as the initial about:blank 2895 // page has not been modified. If so, we must revert to showing about:blank. 2896 // See http://crbug.com/9682. 2897 TEST_F(NavigationControllerTest, ShowRendererURLInNewTabUntilModified) { 2898 NavigationControllerImpl& controller = controller_impl(); 2899 TestNotificationTracker notifications; 2900 RegisterForAllNavNotifications(¬ifications, &controller); 2901 2902 const GURL url("http://foo"); 2903 2904 // For renderer-initiated navigations in new tabs (with no committed entries), 2905 // we show the pending entry's URL as long as the about:blank page is not 2906 // modified. 2907 NavigationController::LoadURLParams load_url_params(url); 2908 load_url_params.transition_type = PAGE_TRANSITION_LINK; 2909 load_url_params.is_renderer_initiated = true; 2910 controller.LoadURLWithParams(load_url_params); 2911 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL()); 2912 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL()); 2913 EXPECT_TRUE( 2914 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())-> 2915 is_renderer_initiated()); 2916 EXPECT_TRUE(controller.IsInitialNavigation()); 2917 EXPECT_FALSE(contents()->HasAccessedInitialDocument()); 2918 2919 // There should be no title yet. 2920 EXPECT_TRUE(contents()->GetTitle().empty()); 2921 2922 // If something else modifies the contents of the about:blank page, then 2923 // we must revert to showing about:blank to avoid a URL spoof. 2924 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0)); 2925 EXPECT_TRUE(contents()->HasAccessedInitialDocument()); 2926 EXPECT_FALSE(controller.GetVisibleEntry()); 2927 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL()); 2928 2929 notifications.Reset(); 2930 } 2931 2932 // Tests that the URLs for browser-initiated navigations in new tabs are 2933 // displayed to the user even after they fail, as long as the initial 2934 // about:blank page has not been modified. If so, we must revert to showing 2935 // about:blank. See http://crbug.com/355537. 2936 TEST_F(NavigationControllerTest, ShowBrowserURLAfterFailUntilModified) { 2937 NavigationControllerImpl& controller = controller_impl(); 2938 TestNotificationTracker notifications; 2939 RegisterForAllNavNotifications(¬ifications, &controller); 2940 2941 const GURL url("http://foo"); 2942 2943 // For browser-initiated navigations in new tabs (with no committed entries), 2944 // we show the pending entry's URL as long as the about:blank page is not 2945 // modified. This is possible in cases that the user types a URL into a popup 2946 // tab created with a slow URL. 2947 NavigationController::LoadURLParams load_url_params(url); 2948 load_url_params.transition_type = PAGE_TRANSITION_TYPED; 2949 load_url_params.is_renderer_initiated = false; 2950 controller.LoadURLWithParams(load_url_params); 2951 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL()); 2952 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL()); 2953 EXPECT_FALSE( 2954 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())-> 2955 is_renderer_initiated()); 2956 EXPECT_TRUE(controller.IsInitialNavigation()); 2957 EXPECT_FALSE(contents()->HasAccessedInitialDocument()); 2958 2959 // There should be no title yet. 2960 EXPECT_TRUE(contents()->GetTitle().empty()); 2961 2962 // Suppose it aborts before committing, if it's a 204 or download or due to a 2963 // stop or a new navigation from the user. The URL should remain visible. 2964 FrameHostMsg_DidFailProvisionalLoadWithError_Params params; 2965 params.error_code = net::ERR_ABORTED; 2966 params.error_description = base::string16(); 2967 params.url = url; 2968 params.showing_repost_interstitial = false; 2969 main_test_rfh()->OnMessageReceived( 2970 FrameHostMsg_DidFailProvisionalLoadWithError(0, params)); 2971 contents()->SetIsLoading(test_rvh(), false, true, NULL); 2972 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL()); 2973 2974 // If something else later modifies the contents of the about:blank page, then 2975 // we must revert to showing about:blank to avoid a URL spoof. 2976 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0)); 2977 EXPECT_TRUE(contents()->HasAccessedInitialDocument()); 2978 EXPECT_FALSE(controller.GetVisibleEntry()); 2979 EXPECT_FALSE(controller.GetPendingEntry()); 2980 2981 notifications.Reset(); 2982 } 2983 2984 // Tests that the URLs for renderer-initiated navigations in new tabs are 2985 // displayed to the user even after they fail, as long as the initial 2986 // about:blank page has not been modified. If so, we must revert to showing 2987 // about:blank. See http://crbug.com/355537. 2988 TEST_F(NavigationControllerTest, ShowRendererURLAfterFailUntilModified) { 2989 NavigationControllerImpl& controller = controller_impl(); 2990 TestNotificationTracker notifications; 2991 RegisterForAllNavNotifications(¬ifications, &controller); 2992 2993 const GURL url("http://foo"); 2994 2995 // For renderer-initiated navigations in new tabs (with no committed entries), 2996 // we show the pending entry's URL as long as the about:blank page is not 2997 // modified. 2998 NavigationController::LoadURLParams load_url_params(url); 2999 load_url_params.transition_type = PAGE_TRANSITION_LINK; 3000 load_url_params.is_renderer_initiated = true; 3001 controller.LoadURLWithParams(load_url_params); 3002 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL()); 3003 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL()); 3004 EXPECT_TRUE( 3005 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())-> 3006 is_renderer_initiated()); 3007 EXPECT_TRUE(controller.IsInitialNavigation()); 3008 EXPECT_FALSE(contents()->HasAccessedInitialDocument()); 3009 3010 // There should be no title yet. 3011 EXPECT_TRUE(contents()->GetTitle().empty()); 3012 3013 // Suppose it aborts before committing, if it's a 204 or download or due to a 3014 // stop or a new navigation from the user. The URL should remain visible. 3015 FrameHostMsg_DidFailProvisionalLoadWithError_Params params; 3016 params.error_code = net::ERR_ABORTED; 3017 params.error_description = base::string16(); 3018 params.url = url; 3019 params.showing_repost_interstitial = false; 3020 main_test_rfh()->OnMessageReceived( 3021 FrameHostMsg_DidFailProvisionalLoadWithError(0, params)); 3022 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL()); 3023 3024 // If something else later modifies the contents of the about:blank page, then 3025 // we must revert to showing about:blank to avoid a URL spoof. 3026 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0)); 3027 EXPECT_TRUE(contents()->HasAccessedInitialDocument()); 3028 EXPECT_FALSE(controller.GetVisibleEntry()); 3029 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL()); 3030 3031 notifications.Reset(); 3032 } 3033 3034 TEST_F(NavigationControllerTest, DontShowRendererURLInNewTabAfterCommit) { 3035 NavigationControllerImpl& controller = controller_impl(); 3036 TestNotificationTracker notifications; 3037 RegisterForAllNavNotifications(¬ifications, &controller); 3038 3039 const GURL url1("http://foo/eh"); 3040 const GURL url2("http://foo/bee"); 3041 3042 // For renderer-initiated navigations in new tabs (with no committed entries), 3043 // we show the pending entry's URL as long as the about:blank page is not 3044 // modified. 3045 NavigationController::LoadURLParams load_url_params(url1); 3046 load_url_params.transition_type = PAGE_TRANSITION_LINK; 3047 load_url_params.is_renderer_initiated = true; 3048 controller.LoadURLWithParams(load_url_params); 3049 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL()); 3050 EXPECT_TRUE( 3051 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())-> 3052 is_renderer_initiated()); 3053 EXPECT_TRUE(controller.IsInitialNavigation()); 3054 EXPECT_FALSE(contents()->HasAccessedInitialDocument()); 3055 3056 // Simulate a commit and then starting a new pending navigation. 3057 main_test_rfh()->SendNavigate(0, url1); 3058 NavigationController::LoadURLParams load_url2_params(url2); 3059 load_url2_params.transition_type = PAGE_TRANSITION_LINK; 3060 load_url2_params.is_renderer_initiated = true; 3061 controller.LoadURLWithParams(load_url2_params); 3062 3063 // We should not consider this an initial navigation, and thus should 3064 // not show the pending URL. 3065 EXPECT_FALSE(contents()->HasAccessedInitialDocument()); 3066 EXPECT_FALSE(controller.IsInitialNavigation()); 3067 EXPECT_TRUE(controller.GetVisibleEntry()); 3068 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL()); 3069 3070 notifications.Reset(); 3071 } 3072 3073 // Tests that IsInPageNavigation returns appropriate results. Prevents 3074 // regression for bug 1126349. 3075 TEST_F(NavigationControllerTest, IsInPageNavigation) { 3076 NavigationControllerImpl& controller = controller_impl(); 3077 // Navigate to URL with no refs. 3078 const GURL url("http://www.google.com/home.html"); 3079 main_test_rfh()->SendNavigate(0, url); 3080 3081 // Reloading the page is not an in-page navigation. 3082 EXPECT_FALSE(controller.IsURLInPageNavigation(url, false, 3083 NAVIGATION_TYPE_UNKNOWN)); 3084 const GURL other_url("http://www.google.com/add.html"); 3085 EXPECT_FALSE(controller.IsURLInPageNavigation(other_url, false, 3086 NAVIGATION_TYPE_UNKNOWN)); 3087 const GURL url_with_ref("http://www.google.com/home.html#my_ref"); 3088 EXPECT_TRUE(controller.IsURLInPageNavigation(url_with_ref, true, 3089 NAVIGATION_TYPE_UNKNOWN)); 3090 3091 // Navigate to URL with refs. 3092 main_test_rfh()->SendNavigate(1, url_with_ref); 3093 3094 // Reloading the page is not an in-page navigation. 3095 EXPECT_FALSE(controller.IsURLInPageNavigation(url_with_ref, false, 3096 NAVIGATION_TYPE_UNKNOWN)); 3097 EXPECT_FALSE(controller.IsURLInPageNavigation(url, false, 3098 NAVIGATION_TYPE_UNKNOWN)); 3099 EXPECT_FALSE(controller.IsURLInPageNavigation(other_url, false, 3100 NAVIGATION_TYPE_UNKNOWN)); 3101 const GURL other_url_with_ref("http://www.google.com/home.html#my_other_ref"); 3102 EXPECT_TRUE(controller.IsURLInPageNavigation(other_url_with_ref, true, 3103 NAVIGATION_TYPE_UNKNOWN)); 3104 3105 // Going to the same url again will be considered in-page 3106 // if the renderer says it is even if the navigation type isn't IN_PAGE. 3107 EXPECT_TRUE(controller.IsURLInPageNavigation(url_with_ref, true, 3108 NAVIGATION_TYPE_UNKNOWN)); 3109 3110 // Going back to the non ref url will be considered in-page if the navigation 3111 // type is IN_PAGE. 3112 EXPECT_TRUE(controller.IsURLInPageNavigation(url, true, 3113 NAVIGATION_TYPE_IN_PAGE)); 3114 3115 // If the renderer says this is a same-origin in-page navigation, believe it. 3116 // This is the pushState/replaceState case. 3117 EXPECT_TRUE(controller.IsURLInPageNavigation(other_url, true, 3118 NAVIGATION_TYPE_UNKNOWN)); 3119 3120 // Don't believe the renderer if it claims a cross-origin navigation is 3121 // in-page. 3122 const GURL different_origin_url("http://www.example.com"); 3123 EXPECT_FALSE(controller.IsURLInPageNavigation(different_origin_url, true, 3124 NAVIGATION_TYPE_UNKNOWN)); 3125 } 3126 3127 // Some pages can have subframes with the same base URL (minus the reference) as 3128 // the main page. Even though this is hard, it can happen, and we don't want 3129 // these subframe navigations to affect the toplevel document. They should 3130 // instead be ignored. http://crbug.com/5585 3131 TEST_F(NavigationControllerTest, SameSubframe) { 3132 NavigationControllerImpl& controller = controller_impl(); 3133 // Navigate the main frame. 3134 const GURL url("http://www.google.com/"); 3135 main_test_rfh()->SendNavigate(0, url); 3136 3137 // We should be at the first navigation entry. 3138 EXPECT_EQ(controller.GetEntryCount(), 1); 3139 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0); 3140 3141 // Navigate a subframe that would normally count as in-page. 3142 const GURL subframe("http://www.google.com/#"); 3143 FrameHostMsg_DidCommitProvisionalLoad_Params params; 3144 params.page_id = 0; 3145 params.url = subframe; 3146 params.transition = PAGE_TRANSITION_AUTO_SUBFRAME; 3147 params.should_update_history = false; 3148 params.gesture = NavigationGestureAuto; 3149 params.is_post = false; 3150 params.page_state = PageState::CreateFromURL(subframe); 3151 LoadCommittedDetails details; 3152 EXPECT_FALSE(controller.RendererDidNavigate(main_test_rfh(), params, 3153 &details)); 3154 3155 // Nothing should have changed. 3156 EXPECT_EQ(controller.GetEntryCount(), 1); 3157 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0); 3158 } 3159 3160 // Make sure that on cloning a WebContentsImpl and going back needs_reload is 3161 // false. 3162 TEST_F(NavigationControllerTest, CloneAndGoBack) { 3163 NavigationControllerImpl& controller = controller_impl(); 3164 const GURL url1("http://foo1"); 3165 const GURL url2("http://foo2"); 3166 const base::string16 title(base::ASCIIToUTF16("Title")); 3167 3168 NavigateAndCommit(url1); 3169 controller.GetVisibleEntry()->SetTitle(title); 3170 NavigateAndCommit(url2); 3171 3172 scoped_ptr<WebContents> clone(controller.GetWebContents()->Clone()); 3173 3174 ASSERT_EQ(2, clone->GetController().GetEntryCount()); 3175 EXPECT_TRUE(clone->GetController().NeedsReload()); 3176 clone->GetController().GoBack(); 3177 // Navigating back should have triggered needs_reload_ to go false. 3178 EXPECT_FALSE(clone->GetController().NeedsReload()); 3179 3180 // Ensure that the pending URL and its title are visible. 3181 EXPECT_EQ(url1, clone->GetController().GetVisibleEntry()->GetURL()); 3182 EXPECT_EQ(title, clone->GetTitle()); 3183 } 3184 3185 // Make sure that reloading a cloned tab doesn't change its pending entry index. 3186 // See http://crbug.com/234491. 3187 TEST_F(NavigationControllerTest, CloneAndReload) { 3188 NavigationControllerImpl& controller = controller_impl(); 3189 const GURL url1("http://foo1"); 3190 const GURL url2("http://foo2"); 3191 const base::string16 title(base::ASCIIToUTF16("Title")); 3192 3193 NavigateAndCommit(url1); 3194 controller.GetVisibleEntry()->SetTitle(title); 3195 NavigateAndCommit(url2); 3196 3197 scoped_ptr<WebContents> clone(controller.GetWebContents()->Clone()); 3198 clone->GetController().LoadIfNecessary(); 3199 3200 ASSERT_EQ(2, clone->GetController().GetEntryCount()); 3201 EXPECT_EQ(1, clone->GetController().GetPendingEntryIndex()); 3202 3203 clone->GetController().Reload(true); 3204 EXPECT_EQ(1, clone->GetController().GetPendingEntryIndex()); 3205 } 3206 3207 // Make sure that cloning a WebContentsImpl doesn't copy interstitials. 3208 TEST_F(NavigationControllerTest, CloneOmitsInterstitials) { 3209 NavigationControllerImpl& controller = controller_impl(); 3210 const GURL url1("http://foo1"); 3211 const GURL url2("http://foo2"); 3212 3213 NavigateAndCommit(url1); 3214 NavigateAndCommit(url2); 3215 3216 // Add an interstitial entry. Should be deleted with controller. 3217 NavigationEntryImpl* interstitial_entry = new NavigationEntryImpl(); 3218 interstitial_entry->set_page_type(PAGE_TYPE_INTERSTITIAL); 3219 controller.SetTransientEntry(interstitial_entry); 3220 3221 scoped_ptr<WebContents> clone(controller.GetWebContents()->Clone()); 3222 3223 ASSERT_EQ(2, clone->GetController().GetEntryCount()); 3224 } 3225 3226 // Test requesting and triggering a lazy reload. 3227 TEST_F(NavigationControllerTest, LazyReload) { 3228 NavigationControllerImpl& controller = controller_impl(); 3229 const GURL url("http://foo"); 3230 NavigateAndCommit(url); 3231 ASSERT_FALSE(controller.NeedsReload()); 3232 3233 // Request a reload to happen when the controller becomes active (e.g. after 3234 // the renderer gets killed in background on Android). 3235 controller.SetNeedsReload(); 3236 ASSERT_TRUE(controller.NeedsReload()); 3237 3238 // Set the controller as active, triggering the requested reload. 3239 controller.SetActive(true); 3240 ASSERT_FALSE(controller.NeedsReload()); 3241 } 3242 3243 // Tests a subframe navigation while a toplevel navigation is pending. 3244 // http://crbug.com/43967 3245 TEST_F(NavigationControllerTest, SubframeWhilePending) { 3246 NavigationControllerImpl& controller = controller_impl(); 3247 // Load the first page. 3248 const GURL url1("http://foo/"); 3249 NavigateAndCommit(url1); 3250 3251 // Now start a pending load to a totally different page, but don't commit it. 3252 const GURL url2("http://bar/"); 3253 controller.LoadURL( 3254 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 3255 3256 // Send a subframe update from the first page, as if one had just 3257 // automatically loaded. Auto subframes don't increment the page ID. 3258 const GURL url1_sub("http://foo/subframe"); 3259 FrameHostMsg_DidCommitProvisionalLoad_Params params; 3260 params.page_id = controller.GetLastCommittedEntry()->GetPageID(); 3261 params.url = url1_sub; 3262 params.transition = PAGE_TRANSITION_AUTO_SUBFRAME; 3263 params.should_update_history = false; 3264 params.gesture = NavigationGestureAuto; 3265 params.is_post = false; 3266 params.page_state = PageState::CreateFromURL(url1_sub); 3267 LoadCommittedDetails details; 3268 3269 // This should return false meaning that nothing was actually updated. 3270 EXPECT_FALSE(controller.RendererDidNavigate(main_test_rfh(), params, 3271 &details)); 3272 3273 // The notification should have updated the last committed one, and not 3274 // the pending load. 3275 EXPECT_EQ(url1, controller.GetLastCommittedEntry()->GetURL()); 3276 3277 // The active entry should be unchanged by the subframe load. 3278 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL()); 3279 } 3280 3281 // Test CopyStateFrom with 2 urls, the first selected and nothing in the target. 3282 TEST_F(NavigationControllerTest, CopyStateFrom) { 3283 NavigationControllerImpl& controller = controller_impl(); 3284 const GURL url1("http://foo1"); 3285 const GURL url2("http://foo2"); 3286 3287 NavigateAndCommit(url1); 3288 NavigateAndCommit(url2); 3289 controller.GoBack(); 3290 contents()->CommitPendingNavigation(); 3291 3292 scoped_ptr<TestWebContents> other_contents( 3293 static_cast<TestWebContents*>(CreateTestWebContents())); 3294 NavigationControllerImpl& other_controller = other_contents->GetController(); 3295 other_controller.CopyStateFrom(controller); 3296 3297 // other_controller should now contain 2 urls. 3298 ASSERT_EQ(2, other_controller.GetEntryCount()); 3299 // We should be looking at the first one. 3300 ASSERT_EQ(0, other_controller.GetCurrentEntryIndex()); 3301 3302 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL()); 3303 EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID()); 3304 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL()); 3305 // This is a different site than url1, so the IDs start again at 0. 3306 EXPECT_EQ(0, other_controller.GetEntryAtIndex(1)->GetPageID()); 3307 3308 // The max page ID map should be copied over and updated with the max page ID 3309 // from the current tab. 3310 SiteInstance* instance1 = 3311 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)); 3312 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1)); 3313 3314 // Ensure the SessionStorageNamespaceMaps are the same size and have 3315 // the same partitons loaded. 3316 // 3317 // TODO(ajwong): We should load a url from a different partition earlier 3318 // to make sure this map has more than one entry. 3319 const SessionStorageNamespaceMap& session_storage_namespace_map = 3320 controller.GetSessionStorageNamespaceMap(); 3321 const SessionStorageNamespaceMap& other_session_storage_namespace_map = 3322 other_controller.GetSessionStorageNamespaceMap(); 3323 EXPECT_EQ(session_storage_namespace_map.size(), 3324 other_session_storage_namespace_map.size()); 3325 for (SessionStorageNamespaceMap::const_iterator it = 3326 session_storage_namespace_map.begin(); 3327 it != session_storage_namespace_map.end(); 3328 ++it) { 3329 SessionStorageNamespaceMap::const_iterator other = 3330 other_session_storage_namespace_map.find(it->first); 3331 EXPECT_TRUE(other != other_session_storage_namespace_map.end()); 3332 } 3333 } 3334 3335 // Tests CopyStateFromAndPrune with 2 urls in source, 1 in dest. 3336 TEST_F(NavigationControllerTest, CopyStateFromAndPrune) { 3337 NavigationControllerImpl& controller = controller_impl(); 3338 const GURL url1("http://foo/1"); 3339 const GURL url2("http://foo/2"); 3340 const GURL url3("http://foo/3"); 3341 3342 NavigateAndCommit(url1); 3343 NavigateAndCommit(url2); 3344 3345 // First two entries should have the same SiteInstance. 3346 SiteInstance* instance1 = 3347 GetSiteInstanceFromEntry(controller.GetEntryAtIndex(0)); 3348 SiteInstance* instance2 = 3349 GetSiteInstanceFromEntry(controller.GetEntryAtIndex(1)); 3350 EXPECT_EQ(instance1, instance2); 3351 EXPECT_EQ(0, controller.GetEntryAtIndex(0)->GetPageID()); 3352 EXPECT_EQ(1, controller.GetEntryAtIndex(1)->GetPageID()); 3353 EXPECT_EQ(1, contents()->GetMaxPageIDForSiteInstance(instance1)); 3354 3355 scoped_ptr<TestWebContents> other_contents( 3356 static_cast<TestWebContents*>(CreateTestWebContents())); 3357 NavigationControllerImpl& other_controller = other_contents->GetController(); 3358 other_contents->NavigateAndCommit(url3); 3359 other_contents->ExpectSetHistoryLengthAndPrune( 3360 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 2, 3361 other_controller.GetEntryAtIndex(0)->GetPageID()); 3362 other_controller.CopyStateFromAndPrune(&controller, false); 3363 3364 // other_controller should now contain the 3 urls: url1, url2 and url3. 3365 3366 ASSERT_EQ(3, other_controller.GetEntryCount()); 3367 3368 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex()); 3369 3370 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL()); 3371 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL()); 3372 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(2)->GetURL()); 3373 EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID()); 3374 EXPECT_EQ(1, other_controller.GetEntryAtIndex(1)->GetPageID()); 3375 EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID()); 3376 3377 // A new SiteInstance in a different BrowsingInstance should be used for the 3378 // new tab. 3379 SiteInstance* instance3 = 3380 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(2)); 3381 EXPECT_NE(instance3, instance1); 3382 EXPECT_FALSE(instance3->IsRelatedSiteInstance(instance1)); 3383 3384 // The max page ID map should be copied over and updated with the max page ID 3385 // from the current tab. 3386 EXPECT_EQ(1, other_contents->GetMaxPageIDForSiteInstance(instance1)); 3387 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance3)); 3388 } 3389 3390 // Test CopyStateFromAndPrune with 2 urls, the first selected and 1 entry in 3391 // the target. 3392 TEST_F(NavigationControllerTest, CopyStateFromAndPrune2) { 3393 NavigationControllerImpl& controller = controller_impl(); 3394 const GURL url1("http://foo1"); 3395 const GURL url2("http://foo2"); 3396 const GURL url3("http://foo3"); 3397 3398 NavigateAndCommit(url1); 3399 NavigateAndCommit(url2); 3400 controller.GoBack(); 3401 contents()->CommitPendingNavigation(); 3402 3403 scoped_ptr<TestWebContents> other_contents( 3404 static_cast<TestWebContents*>(CreateTestWebContents())); 3405 NavigationControllerImpl& other_controller = other_contents->GetController(); 3406 other_contents->NavigateAndCommit(url3); 3407 other_contents->ExpectSetHistoryLengthAndPrune( 3408 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 1, 3409 other_controller.GetEntryAtIndex(0)->GetPageID()); 3410 other_controller.CopyStateFromAndPrune(&controller, false); 3411 3412 // other_controller should now contain: url1, url3 3413 3414 ASSERT_EQ(2, other_controller.GetEntryCount()); 3415 ASSERT_EQ(1, other_controller.GetCurrentEntryIndex()); 3416 3417 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL()); 3418 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL()); 3419 EXPECT_EQ(0, other_controller.GetEntryAtIndex(1)->GetPageID()); 3420 3421 // The max page ID map should be copied over and updated with the max page ID 3422 // from the current tab. 3423 SiteInstance* instance1 = 3424 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(1)); 3425 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1)); 3426 } 3427 3428 // Test CopyStateFromAndPrune with 2 urls, the last selected and 2 entries in 3429 // the target. 3430 TEST_F(NavigationControllerTest, CopyStateFromAndPrune3) { 3431 NavigationControllerImpl& controller = controller_impl(); 3432 const GURL url1("http://foo1"); 3433 const GURL url2("http://foo2"); 3434 const GURL url3("http://foo3"); 3435 const GURL url4("http://foo4"); 3436 3437 NavigateAndCommit(url1); 3438 NavigateAndCommit(url2); 3439 3440 scoped_ptr<TestWebContents> other_contents( 3441 static_cast<TestWebContents*>(CreateTestWebContents())); 3442 NavigationControllerImpl& other_controller = other_contents->GetController(); 3443 other_contents->NavigateAndCommit(url3); 3444 other_contents->NavigateAndCommit(url4); 3445 other_contents->ExpectSetHistoryLengthAndPrune( 3446 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(1)), 2, 3447 other_controller.GetEntryAtIndex(0)->GetPageID()); 3448 other_controller.CopyStateFromAndPrune(&controller, false); 3449 3450 // other_controller should now contain: url1, url2, url4 3451 3452 ASSERT_EQ(3, other_controller.GetEntryCount()); 3453 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex()); 3454 3455 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL()); 3456 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL()); 3457 EXPECT_EQ(url4, other_controller.GetEntryAtIndex(2)->GetURL()); 3458 3459 // The max page ID map should be copied over and updated with the max page ID 3460 // from the current tab. 3461 SiteInstance* instance1 = 3462 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(2)); 3463 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1)); 3464 } 3465 3466 // Test CopyStateFromAndPrune with 2 urls, 2 entries in the target, with 3467 // not the last entry selected in the target. 3468 TEST_F(NavigationControllerTest, CopyStateFromAndPruneNotLast) { 3469 NavigationControllerImpl& controller = controller_impl(); 3470 const GURL url1("http://foo1"); 3471 const GURL url2("http://foo2"); 3472 const GURL url3("http://foo3"); 3473 const GURL url4("http://foo4"); 3474 3475 NavigateAndCommit(url1); 3476 NavigateAndCommit(url2); 3477 3478 scoped_ptr<TestWebContents> other_contents( 3479 static_cast<TestWebContents*>(CreateTestWebContents())); 3480 NavigationControllerImpl& other_controller = other_contents->GetController(); 3481 other_contents->NavigateAndCommit(url3); 3482 other_contents->NavigateAndCommit(url4); 3483 other_controller.GoBack(); 3484 other_contents->CommitPendingNavigation(); 3485 other_contents->ExpectSetHistoryLengthAndPrune( 3486 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 2, 3487 other_controller.GetEntryAtIndex(0)->GetPageID()); 3488 other_controller.CopyStateFromAndPrune(&controller, false); 3489 3490 // other_controller should now contain: url1, url2, url3 3491 3492 ASSERT_EQ(3, other_controller.GetEntryCount()); 3493 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex()); 3494 3495 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL()); 3496 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL()); 3497 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(2)->GetURL()); 3498 3499 // The max page ID map should be copied over and updated with the max page ID 3500 // from the current tab. 3501 SiteInstance* instance1 = 3502 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(2)); 3503 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1)); 3504 } 3505 3506 // Test CopyStateFromAndPrune with 2 urls, the first selected and 1 entry plus 3507 // a pending entry in the target. 3508 TEST_F(NavigationControllerTest, CopyStateFromAndPruneTargetPending) { 3509 NavigationControllerImpl& controller = controller_impl(); 3510 const GURL url1("http://foo1"); 3511 const GURL url2("http://foo2"); 3512 const GURL url3("http://foo3"); 3513 const GURL url4("http://foo4"); 3514 3515 NavigateAndCommit(url1); 3516 NavigateAndCommit(url2); 3517 controller.GoBack(); 3518 contents()->CommitPendingNavigation(); 3519 3520 scoped_ptr<TestWebContents> other_contents( 3521 static_cast<TestWebContents*>(CreateTestWebContents())); 3522 NavigationControllerImpl& other_controller = other_contents->GetController(); 3523 other_contents->NavigateAndCommit(url3); 3524 other_controller.LoadURL( 3525 url4, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 3526 other_contents->ExpectSetHistoryLengthAndPrune( 3527 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 1, 3528 other_controller.GetEntryAtIndex(0)->GetPageID()); 3529 other_controller.CopyStateFromAndPrune(&controller, false); 3530 3531 // other_controller should now contain url1, url3, and a pending entry 3532 // for url4. 3533 3534 ASSERT_EQ(2, other_controller.GetEntryCount()); 3535 EXPECT_EQ(1, other_controller.GetCurrentEntryIndex()); 3536 3537 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL()); 3538 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL()); 3539 3540 // And there should be a pending entry for url4. 3541 ASSERT_TRUE(other_controller.GetPendingEntry()); 3542 EXPECT_EQ(url4, other_controller.GetPendingEntry()->GetURL()); 3543 3544 // The max page ID map should be copied over and updated with the max page ID 3545 // from the current tab. 3546 SiteInstance* instance1 = 3547 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)); 3548 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1)); 3549 } 3550 3551 // Test CopyStateFromAndPrune with 1 url in the source, 1 entry and a pending 3552 // client redirect entry (with the same page ID) in the target. This used to 3553 // crash because the last committed entry would be pruned but max_page_id 3554 // remembered the page ID (http://crbug.com/234809). 3555 TEST_F(NavigationControllerTest, CopyStateFromAndPruneTargetPending2) { 3556 NavigationControllerImpl& controller = controller_impl(); 3557 const GURL url1("http://foo1"); 3558 const GURL url2a("http://foo2/a"); 3559 const GURL url2b("http://foo2/b"); 3560 3561 NavigateAndCommit(url1); 3562 3563 scoped_ptr<TestWebContents> other_contents( 3564 static_cast<TestWebContents*>(CreateTestWebContents())); 3565 NavigationControllerImpl& other_controller = other_contents->GetController(); 3566 other_contents->NavigateAndCommit(url2a); 3567 // Simulate a client redirect, which has the same page ID as entry 2a. 3568 other_controller.LoadURL( 3569 url2b, Referrer(), PAGE_TRANSITION_LINK, std::string()); 3570 other_controller.GetPendingEntry()->SetPageID( 3571 other_controller.GetLastCommittedEntry()->GetPageID()); 3572 3573 other_contents->ExpectSetHistoryLengthAndPrune( 3574 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 1, 3575 other_controller.GetEntryAtIndex(0)->GetPageID()); 3576 other_controller.CopyStateFromAndPrune(&controller, false); 3577 3578 // other_controller should now contain url1, url2a, and a pending entry 3579 // for url2b. 3580 3581 ASSERT_EQ(2, other_controller.GetEntryCount()); 3582 EXPECT_EQ(1, other_controller.GetCurrentEntryIndex()); 3583 3584 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL()); 3585 EXPECT_EQ(url2a, other_controller.GetEntryAtIndex(1)->GetURL()); 3586 3587 // And there should be a pending entry for url4. 3588 ASSERT_TRUE(other_controller.GetPendingEntry()); 3589 EXPECT_EQ(url2b, other_controller.GetPendingEntry()->GetURL()); 3590 3591 // Let the pending entry commit. 3592 other_contents->CommitPendingNavigation(); 3593 3594 // The max page ID map should be copied over and updated with the max page ID 3595 // from the current tab. 3596 SiteInstance* instance1 = 3597 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(1)); 3598 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1)); 3599 } 3600 3601 // Test CopyStateFromAndPrune with 2 urls, a back navigation pending in the 3602 // source, and 1 entry in the target. The back pending entry should be ignored. 3603 TEST_F(NavigationControllerTest, CopyStateFromAndPruneSourcePending) { 3604 NavigationControllerImpl& controller = controller_impl(); 3605 const GURL url1("http://foo1"); 3606 const GURL url2("http://foo2"); 3607 const GURL url3("http://foo3"); 3608 3609 NavigateAndCommit(url1); 3610 NavigateAndCommit(url2); 3611 controller.GoBack(); 3612 3613 scoped_ptr<TestWebContents> other_contents( 3614 static_cast<TestWebContents*>(CreateTestWebContents())); 3615 NavigationControllerImpl& other_controller = other_contents->GetController(); 3616 other_contents->NavigateAndCommit(url3); 3617 other_contents->ExpectSetHistoryLengthAndPrune( 3618 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 2, 3619 other_controller.GetEntryAtIndex(0)->GetPageID()); 3620 other_controller.CopyStateFromAndPrune(&controller, false); 3621 3622 // other_controller should now contain: url1, url2, url3 3623 3624 ASSERT_EQ(3, other_controller.GetEntryCount()); 3625 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex()); 3626 3627 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL()); 3628 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL()); 3629 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(2)->GetURL()); 3630 EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID()); 3631 3632 // The max page ID map should be copied over and updated with the max page ID 3633 // from the current tab. 3634 SiteInstance* instance1 = 3635 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(2)); 3636 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1)); 3637 } 3638 3639 // Tests CopyStateFromAndPrune with 3 urls in source, 1 in dest, 3640 // when the max entry count is 3. We should prune one entry. 3641 TEST_F(NavigationControllerTest, CopyStateFromAndPruneMaxEntries) { 3642 NavigationControllerImpl& controller = controller_impl(); 3643 size_t original_count = NavigationControllerImpl::max_entry_count(); 3644 const int kMaxEntryCount = 3; 3645 3646 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount); 3647 3648 const GURL url1("http://foo/1"); 3649 const GURL url2("http://foo/2"); 3650 const GURL url3("http://foo/3"); 3651 const GURL url4("http://foo/4"); 3652 3653 // Create a PrunedListener to observe prune notifications. 3654 PrunedListener listener(&controller); 3655 3656 NavigateAndCommit(url1); 3657 NavigateAndCommit(url2); 3658 NavigateAndCommit(url3); 3659 3660 scoped_ptr<TestWebContents> other_contents( 3661 static_cast<TestWebContents*>(CreateTestWebContents())); 3662 NavigationControllerImpl& other_controller = other_contents->GetController(); 3663 other_contents->NavigateAndCommit(url4); 3664 other_contents->ExpectSetHistoryLengthAndPrune( 3665 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 2, 3666 other_controller.GetEntryAtIndex(0)->GetPageID()); 3667 other_controller.CopyStateFromAndPrune(&controller, false); 3668 3669 // We should have received a pruned notification. 3670 EXPECT_EQ(1, listener.notification_count_); 3671 EXPECT_TRUE(listener.details_.from_front); 3672 EXPECT_EQ(1, listener.details_.count); 3673 3674 // other_controller should now contain only 3 urls: url2, url3 and url4. 3675 3676 ASSERT_EQ(3, other_controller.GetEntryCount()); 3677 3678 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex()); 3679 3680 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(0)->GetURL()); 3681 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL()); 3682 EXPECT_EQ(url4, other_controller.GetEntryAtIndex(2)->GetURL()); 3683 EXPECT_EQ(1, other_controller.GetEntryAtIndex(0)->GetPageID()); 3684 EXPECT_EQ(2, other_controller.GetEntryAtIndex(1)->GetPageID()); 3685 EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID()); 3686 3687 NavigationControllerImpl::set_max_entry_count_for_testing(original_count); 3688 } 3689 3690 // Tests CopyStateFromAndPrune with 2 urls in source, 1 in dest, with 3691 // replace_entry set to true. 3692 TEST_F(NavigationControllerTest, CopyStateFromAndPruneReplaceEntry) { 3693 NavigationControllerImpl& controller = controller_impl(); 3694 const GURL url1("http://foo/1"); 3695 const GURL url2("http://foo/2"); 3696 const GURL url3("http://foo/3"); 3697 3698 NavigateAndCommit(url1); 3699 NavigateAndCommit(url2); 3700 3701 // First two entries should have the same SiteInstance. 3702 SiteInstance* instance1 = 3703 GetSiteInstanceFromEntry(controller.GetEntryAtIndex(0)); 3704 SiteInstance* instance2 = 3705 GetSiteInstanceFromEntry(controller.GetEntryAtIndex(1)); 3706 EXPECT_EQ(instance1, instance2); 3707 EXPECT_EQ(0, controller.GetEntryAtIndex(0)->GetPageID()); 3708 EXPECT_EQ(1, controller.GetEntryAtIndex(1)->GetPageID()); 3709 EXPECT_EQ(1, contents()->GetMaxPageIDForSiteInstance(instance1)); 3710 3711 scoped_ptr<TestWebContents> other_contents( 3712 static_cast<TestWebContents*>(CreateTestWebContents())); 3713 NavigationControllerImpl& other_controller = other_contents->GetController(); 3714 other_contents->NavigateAndCommit(url3); 3715 other_contents->ExpectSetHistoryLengthAndPrune( 3716 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 1, 3717 other_controller.GetEntryAtIndex(0)->GetPageID()); 3718 other_controller.CopyStateFromAndPrune(&controller, true); 3719 3720 // other_controller should now contain the 2 urls: url1 and url3. 3721 3722 ASSERT_EQ(2, other_controller.GetEntryCount()); 3723 3724 ASSERT_EQ(1, other_controller.GetCurrentEntryIndex()); 3725 3726 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL()); 3727 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL()); 3728 EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID()); 3729 EXPECT_EQ(0, other_controller.GetEntryAtIndex(1)->GetPageID()); 3730 3731 // A new SiteInstance in a different BrowsingInstance should be used for the 3732 // new tab. 3733 SiteInstance* instance3 = 3734 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(1)); 3735 EXPECT_NE(instance3, instance1); 3736 EXPECT_FALSE(instance3->IsRelatedSiteInstance(instance1)); 3737 3738 // The max page ID map should be copied over and updated with the max page ID 3739 // from the current tab. 3740 EXPECT_EQ(1, other_contents->GetMaxPageIDForSiteInstance(instance1)); 3741 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance3)); 3742 } 3743 3744 // Tests CopyStateFromAndPrune with 3 urls in source, 1 in dest, when the max 3745 // entry count is 3 and replace_entry is true. We should not prune entries. 3746 TEST_F(NavigationControllerTest, CopyStateFromAndPruneMaxEntriesReplaceEntry) { 3747 NavigationControllerImpl& controller = controller_impl(); 3748 size_t original_count = NavigationControllerImpl::max_entry_count(); 3749 const int kMaxEntryCount = 3; 3750 3751 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount); 3752 3753 const GURL url1("http://foo/1"); 3754 const GURL url2("http://foo/2"); 3755 const GURL url3("http://foo/3"); 3756 const GURL url4("http://foo/4"); 3757 3758 // Create a PrunedListener to observe prune notifications. 3759 PrunedListener listener(&controller); 3760 3761 NavigateAndCommit(url1); 3762 NavigateAndCommit(url2); 3763 NavigateAndCommit(url3); 3764 3765 scoped_ptr<TestWebContents> other_contents( 3766 static_cast<TestWebContents*>(CreateTestWebContents())); 3767 NavigationControllerImpl& other_controller = other_contents->GetController(); 3768 other_contents->NavigateAndCommit(url4); 3769 other_contents->ExpectSetHistoryLengthAndPrune( 3770 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 2, 3771 other_controller.GetEntryAtIndex(0)->GetPageID()); 3772 other_controller.CopyStateFromAndPrune(&controller, true); 3773 3774 // We should have received no pruned notification. 3775 EXPECT_EQ(0, listener.notification_count_); 3776 3777 // other_controller should now contain only 3 urls: url1, url2 and url4. 3778 3779 ASSERT_EQ(3, other_controller.GetEntryCount()); 3780 3781 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex()); 3782 3783 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL()); 3784 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL()); 3785 EXPECT_EQ(url4, other_controller.GetEntryAtIndex(2)->GetURL()); 3786 EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID()); 3787 EXPECT_EQ(1, other_controller.GetEntryAtIndex(1)->GetPageID()); 3788 EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID()); 3789 3790 NavigationControllerImpl::set_max_entry_count_for_testing(original_count); 3791 } 3792 3793 // Tests that we can navigate to the restored entries 3794 // imported by CopyStateFromAndPrune. 3795 TEST_F(NavigationControllerTest, CopyRestoredStateAndNavigate) { 3796 const GURL kRestoredUrls[] = { 3797 GURL("http://site1.com"), 3798 GURL("http://site2.com"), 3799 }; 3800 const GURL kInitialUrl("http://site3.com"); 3801 3802 std::vector<NavigationEntry*> entries; 3803 for (size_t i = 0; i < arraysize(kRestoredUrls); ++i) { 3804 NavigationEntry* entry = NavigationControllerImpl::CreateNavigationEntry( 3805 kRestoredUrls[i], Referrer(), PAGE_TRANSITION_RELOAD, false, 3806 std::string(), browser_context()); 3807 entry->SetPageID(static_cast<int>(i)); 3808 entries.push_back(entry); 3809 } 3810 3811 // Create a WebContents with restored entries. 3812 scoped_ptr<TestWebContents> source_contents( 3813 static_cast<TestWebContents*>(CreateTestWebContents())); 3814 NavigationControllerImpl& source_controller = 3815 source_contents->GetController(); 3816 source_controller.Restore( 3817 entries.size() - 1, 3818 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY, 3819 &entries); 3820 ASSERT_EQ(0u, entries.size()); 3821 source_controller.LoadIfNecessary(); 3822 source_contents->CommitPendingNavigation(); 3823 3824 // Load a page, then copy state from |source_contents|. 3825 NavigateAndCommit(kInitialUrl); 3826 contents()->ExpectSetHistoryLengthAndPrune( 3827 GetSiteInstanceFromEntry(controller_impl().GetEntryAtIndex(0)), 2, 3828 controller_impl().GetEntryAtIndex(0)->GetPageID()); 3829 controller_impl().CopyStateFromAndPrune(&source_controller, false); 3830 ASSERT_EQ(3, controller_impl().GetEntryCount()); 3831 3832 // Go back to the first entry one at a time and 3833 // verify that it works as expected. 3834 EXPECT_EQ(2, controller_impl().GetCurrentEntryIndex()); 3835 EXPECT_EQ(kInitialUrl, controller_impl().GetActiveEntry()->GetURL()); 3836 3837 controller_impl().GoBack(); 3838 contents()->CommitPendingNavigation(); 3839 EXPECT_EQ(1, controller_impl().GetCurrentEntryIndex()); 3840 EXPECT_EQ(kRestoredUrls[1], controller_impl().GetActiveEntry()->GetURL()); 3841 3842 controller_impl().GoBack(); 3843 contents()->CommitPendingNavigation(); 3844 EXPECT_EQ(0, controller_impl().GetCurrentEntryIndex()); 3845 EXPECT_EQ(kRestoredUrls[0], controller_impl().GetActiveEntry()->GetURL()); 3846 } 3847 3848 // Tests that navigations initiated from the page (with the history object) 3849 // work as expected, creating pending entries. 3850 TEST_F(NavigationControllerTest, HistoryNavigate) { 3851 NavigationControllerImpl& controller = controller_impl(); 3852 const GURL url1("http://foo/1"); 3853 const GURL url2("http://foo/2"); 3854 const GURL url3("http://foo/3"); 3855 3856 NavigateAndCommit(url1); 3857 NavigateAndCommit(url2); 3858 NavigateAndCommit(url3); 3859 controller.GoBack(); 3860 contents()->CommitPendingNavigation(); 3861 3862 // Simulate the page calling history.back(). It should create a pending entry. 3863 contents()->OnGoToEntryAtOffset(-1); 3864 EXPECT_EQ(0, controller.GetPendingEntryIndex()); 3865 // The actual cross-navigation is suspended until the current RVH tells us 3866 // it unloaded, simulate that. 3867 contents()->ProceedWithCrossSiteNavigation(); 3868 // Also make sure we told the page to navigate. 3869 const IPC::Message* message = 3870 process()->sink().GetFirstMessageMatching(FrameMsg_Navigate::ID); 3871 ASSERT_TRUE(message != NULL); 3872 Tuple1<FrameMsg_Navigate_Params> nav_params; 3873 FrameMsg_Navigate::Read(message, &nav_params); 3874 EXPECT_EQ(url1, nav_params.a.url); 3875 process()->sink().ClearMessages(); 3876 3877 // Now test history.forward() 3878 contents()->OnGoToEntryAtOffset(2); 3879 EXPECT_EQ(2, controller.GetPendingEntryIndex()); 3880 // The actual cross-navigation is suspended until the current RVH tells us 3881 // it unloaded, simulate that. 3882 contents()->ProceedWithCrossSiteNavigation(); 3883 message = process()->sink().GetFirstMessageMatching(FrameMsg_Navigate::ID); 3884 ASSERT_TRUE(message != NULL); 3885 FrameMsg_Navigate::Read(message, &nav_params); 3886 EXPECT_EQ(url3, nav_params.a.url); 3887 process()->sink().ClearMessages(); 3888 3889 controller.DiscardNonCommittedEntries(); 3890 3891 // Make sure an extravagant history.go() doesn't break. 3892 contents()->OnGoToEntryAtOffset(120); // Out of bounds. 3893 EXPECT_EQ(-1, controller.GetPendingEntryIndex()); 3894 message = process()->sink().GetFirstMessageMatching(FrameMsg_Navigate::ID); 3895 EXPECT_TRUE(message == NULL); 3896 } 3897 3898 // Test call to PruneAllButLastCommitted for the only entry. 3899 TEST_F(NavigationControllerTest, PruneAllButLastCommittedForSingle) { 3900 NavigationControllerImpl& controller = controller_impl(); 3901 const GURL url1("http://foo1"); 3902 NavigateAndCommit(url1); 3903 3904 contents()->ExpectSetHistoryLengthAndPrune( 3905 GetSiteInstanceFromEntry(controller.GetEntryAtIndex(0)), 0, 3906 controller.GetEntryAtIndex(0)->GetPageID()); 3907 3908 controller.PruneAllButLastCommitted(); 3909 3910 EXPECT_EQ(-1, controller.GetPendingEntryIndex()); 3911 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url1); 3912 } 3913 3914 // Test call to PruneAllButLastCommitted for first entry. 3915 TEST_F(NavigationControllerTest, PruneAllButLastCommittedForFirst) { 3916 NavigationControllerImpl& controller = controller_impl(); 3917 const GURL url1("http://foo/1"); 3918 const GURL url2("http://foo/2"); 3919 const GURL url3("http://foo/3"); 3920 3921 NavigateAndCommit(url1); 3922 NavigateAndCommit(url2); 3923 NavigateAndCommit(url3); 3924 controller.GoBack(); 3925 controller.GoBack(); 3926 contents()->CommitPendingNavigation(); 3927 3928 contents()->ExpectSetHistoryLengthAndPrune( 3929 GetSiteInstanceFromEntry(controller.GetEntryAtIndex(0)), 0, 3930 controller.GetEntryAtIndex(0)->GetPageID()); 3931 3932 controller.PruneAllButLastCommitted(); 3933 3934 EXPECT_EQ(-1, controller.GetPendingEntryIndex()); 3935 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url1); 3936 } 3937 3938 // Test call to PruneAllButLastCommitted for intermediate entry. 3939 TEST_F(NavigationControllerTest, PruneAllButLastCommittedForIntermediate) { 3940 NavigationControllerImpl& controller = controller_impl(); 3941 const GURL url1("http://foo/1"); 3942 const GURL url2("http://foo/2"); 3943 const GURL url3("http://foo/3"); 3944 3945 NavigateAndCommit(url1); 3946 NavigateAndCommit(url2); 3947 NavigateAndCommit(url3); 3948 controller.GoBack(); 3949 contents()->CommitPendingNavigation(); 3950 3951 contents()->ExpectSetHistoryLengthAndPrune( 3952 GetSiteInstanceFromEntry(controller.GetEntryAtIndex(1)), 0, 3953 controller.GetEntryAtIndex(1)->GetPageID()); 3954 3955 controller.PruneAllButLastCommitted(); 3956 3957 EXPECT_EQ(-1, controller.GetPendingEntryIndex()); 3958 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url2); 3959 } 3960 3961 // Test call to PruneAllButLastCommitted for a pending entry that is not yet in 3962 // the list of entries. 3963 TEST_F(NavigationControllerTest, PruneAllButLastCommittedForPendingNotInList) { 3964 NavigationControllerImpl& controller = controller_impl(); 3965 const GURL url1("http://foo/1"); 3966 const GURL url2("http://foo/2"); 3967 const GURL url3("http://foo/3"); 3968 3969 NavigateAndCommit(url1); 3970 NavigateAndCommit(url2); 3971 3972 // Create a pending entry that is not in the entry list. 3973 controller.LoadURL( 3974 url3, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 3975 EXPECT_TRUE(controller.GetPendingEntry()); 3976 EXPECT_EQ(2, controller.GetEntryCount()); 3977 3978 contents()->ExpectSetHistoryLengthAndPrune( 3979 NULL, 0, controller.GetPendingEntry()->GetPageID()); 3980 controller.PruneAllButLastCommitted(); 3981 3982 // We should only have the last committed and pending entries at this point, 3983 // and the pending entry should still not be in the entry list. 3984 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex()); 3985 EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL()); 3986 EXPECT_EQ(-1, controller.GetPendingEntryIndex()); 3987 EXPECT_TRUE(controller.GetPendingEntry()); 3988 EXPECT_EQ(1, controller.GetEntryCount()); 3989 3990 // Try to commit the pending entry. 3991 main_test_rfh()->SendNavigate(2, url3); 3992 EXPECT_EQ(-1, controller.GetPendingEntryIndex()); 3993 EXPECT_FALSE(controller.GetPendingEntry()); 3994 EXPECT_EQ(2, controller.GetEntryCount()); 3995 EXPECT_EQ(url3, controller.GetEntryAtIndex(1)->GetURL()); 3996 } 3997 3998 // Test to ensure that when we do a history navigation back to the current 3999 // committed page (e.g., going forward to a slow-loading page, then pressing 4000 // the back button), we just stop the navigation to prevent the throbber from 4001 // running continuously. Otherwise, the RenderViewHost forces the throbber to 4002 // start, but WebKit essentially ignores the navigation and never sends a 4003 // message to stop the throbber. 4004 TEST_F(NavigationControllerTest, StopOnHistoryNavigationToCurrentPage) { 4005 NavigationControllerImpl& controller = controller_impl(); 4006 const GURL url0("http://foo/0"); 4007 const GURL url1("http://foo/1"); 4008 4009 NavigateAndCommit(url0); 4010 NavigateAndCommit(url1); 4011 4012 // Go back to the original page, then forward to the slow page, then back 4013 controller.GoBack(); 4014 contents()->CommitPendingNavigation(); 4015 4016 controller.GoForward(); 4017 EXPECT_EQ(1, controller.GetPendingEntryIndex()); 4018 4019 controller.GoBack(); 4020 EXPECT_EQ(-1, controller.GetPendingEntryIndex()); 4021 } 4022 4023 TEST_F(NavigationControllerTest, IsInitialNavigation) { 4024 NavigationControllerImpl& controller = controller_impl(); 4025 TestNotificationTracker notifications; 4026 RegisterForAllNavNotifications(¬ifications, &controller); 4027 4028 // Initial state. 4029 EXPECT_TRUE(controller.IsInitialNavigation()); 4030 4031 // After commit, it stays false. 4032 const GURL url1("http://foo1"); 4033 main_test_rfh()->SendNavigate(0, url1); 4034 EXPECT_EQ(1U, navigation_entry_committed_counter_); 4035 navigation_entry_committed_counter_ = 0; 4036 EXPECT_FALSE(controller.IsInitialNavigation()); 4037 4038 // After starting a new navigation, it stays false. 4039 const GURL url2("http://foo2"); 4040 controller.LoadURL( 4041 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 4042 } 4043 4044 // Check that the favicon is not reused across a client redirect. 4045 // (crbug.com/28515) 4046 TEST_F(NavigationControllerTest, ClearFaviconOnRedirect) { 4047 const GURL kPageWithFavicon("http://withfavicon.html"); 4048 const GURL kPageWithoutFavicon("http://withoutfavicon.html"); 4049 const GURL kIconURL("http://withfavicon.ico"); 4050 const gfx::Image kDefaultFavicon = FaviconStatus().image; 4051 4052 NavigationControllerImpl& controller = controller_impl(); 4053 TestNotificationTracker notifications; 4054 RegisterForAllNavNotifications(¬ifications, &controller); 4055 4056 main_test_rfh()->SendNavigate(0, kPageWithFavicon); 4057 EXPECT_EQ(1U, navigation_entry_committed_counter_); 4058 navigation_entry_committed_counter_ = 0; 4059 4060 NavigationEntry* entry = controller.GetLastCommittedEntry(); 4061 EXPECT_TRUE(entry); 4062 EXPECT_EQ(kPageWithFavicon, entry->GetURL()); 4063 4064 // Simulate Chromium having set the favicon for |kPageWithFavicon|. 4065 content::FaviconStatus& favicon_status = entry->GetFavicon(); 4066 favicon_status.image = CreateImage(SK_ColorWHITE); 4067 favicon_status.url = kIconURL; 4068 favicon_status.valid = true; 4069 EXPECT_FALSE(DoImagesMatch(kDefaultFavicon, entry->GetFavicon().image)); 4070 4071 main_test_rfh()->SendNavigateWithTransition( 4072 0, // same page ID. 4073 kPageWithoutFavicon, 4074 PAGE_TRANSITION_CLIENT_REDIRECT); 4075 EXPECT_EQ(1U, navigation_entry_committed_counter_); 4076 navigation_entry_committed_counter_ = 0; 4077 4078 entry = controller.GetLastCommittedEntry(); 4079 EXPECT_TRUE(entry); 4080 EXPECT_EQ(kPageWithoutFavicon, entry->GetURL()); 4081 4082 EXPECT_TRUE(DoImagesMatch(kDefaultFavicon, entry->GetFavicon().image)); 4083 } 4084 4085 // Check that the favicon is not cleared for NavigationEntries which were 4086 // previously navigated to. 4087 TEST_F(NavigationControllerTest, BackNavigationDoesNotClearFavicon) { 4088 const GURL kUrl1("http://www.a.com/1"); 4089 const GURL kUrl2("http://www.a.com/2"); 4090 const GURL kIconURL("http://www.a.com/1/favicon.ico"); 4091 4092 NavigationControllerImpl& controller = controller_impl(); 4093 TestNotificationTracker notifications; 4094 RegisterForAllNavNotifications(¬ifications, &controller); 4095 4096 main_test_rfh()->SendNavigate(0, kUrl1); 4097 EXPECT_EQ(1U, navigation_entry_committed_counter_); 4098 navigation_entry_committed_counter_ = 0; 4099 4100 // Simulate Chromium having set the favicon for |kUrl1|. 4101 gfx::Image favicon_image = CreateImage(SK_ColorWHITE); 4102 content::NavigationEntry* entry = controller.GetLastCommittedEntry(); 4103 EXPECT_TRUE(entry); 4104 content::FaviconStatus& favicon_status = entry->GetFavicon(); 4105 favicon_status.image = favicon_image; 4106 favicon_status.url = kIconURL; 4107 favicon_status.valid = true; 4108 4109 // Navigate to another page and go back to the original page. 4110 main_test_rfh()->SendNavigate(1, kUrl2); 4111 EXPECT_EQ(1U, navigation_entry_committed_counter_); 4112 navigation_entry_committed_counter_ = 0; 4113 main_test_rfh()->SendNavigateWithTransition( 4114 0, 4115 kUrl1, 4116 PAGE_TRANSITION_FORWARD_BACK); 4117 EXPECT_EQ(1U, navigation_entry_committed_counter_); 4118 navigation_entry_committed_counter_ = 0; 4119 4120 // Verify that the favicon for the page at |kUrl1| was not cleared. 4121 entry = controller.GetEntryAtIndex(0); 4122 EXPECT_TRUE(entry); 4123 EXPECT_EQ(kUrl1, entry->GetURL()); 4124 EXPECT_TRUE(DoImagesMatch(favicon_image, entry->GetFavicon().image)); 4125 } 4126 4127 // The test crashes on android: http://crbug.com/170449 4128 #if defined(OS_ANDROID) 4129 #define MAYBE_PurgeScreenshot DISABLED_PurgeScreenshot 4130 #else 4131 #define MAYBE_PurgeScreenshot PurgeScreenshot 4132 #endif 4133 // Tests that screenshot are purged correctly. 4134 TEST_F(NavigationControllerTest, MAYBE_PurgeScreenshot) { 4135 NavigationControllerImpl& controller = controller_impl(); 4136 4137 NavigationEntryImpl* entry; 4138 4139 // Navigate enough times to make sure that some screenshots are purged. 4140 for (int i = 0; i < 12; ++i) { 4141 const GURL url(base::StringPrintf("http://foo%d/", i)); 4142 NavigateAndCommit(url); 4143 EXPECT_EQ(i, controller.GetCurrentEntryIndex()); 4144 } 4145 4146 MockScreenshotManager* screenshot_manager = 4147 new MockScreenshotManager(&controller); 4148 controller.SetScreenshotManager(screenshot_manager); 4149 for (int i = 0; i < controller.GetEntryCount(); ++i) { 4150 entry = NavigationEntryImpl::FromNavigationEntry( 4151 controller.GetEntryAtIndex(i)); 4152 screenshot_manager->TakeScreenshotFor(entry); 4153 EXPECT_TRUE(entry->screenshot().get()); 4154 } 4155 4156 NavigateAndCommit(GURL("https://foo/")); 4157 EXPECT_EQ(13, controller.GetEntryCount()); 4158 entry = NavigationEntryImpl::FromNavigationEntry( 4159 controller.GetEntryAtIndex(11)); 4160 screenshot_manager->TakeScreenshotFor(entry); 4161 4162 for (int i = 0; i < 2; ++i) { 4163 entry = NavigationEntryImpl::FromNavigationEntry( 4164 controller.GetEntryAtIndex(i)); 4165 EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i 4166 << " not purged"; 4167 } 4168 4169 for (int i = 2; i < controller.GetEntryCount() - 1; ++i) { 4170 entry = NavigationEntryImpl::FromNavigationEntry( 4171 controller.GetEntryAtIndex(i)); 4172 EXPECT_TRUE(entry->screenshot().get()) << "Screenshot not found for " << i; 4173 } 4174 4175 // Navigate to index 5 and then try to assign screenshot to all entries. 4176 controller.GoToIndex(5); 4177 contents()->CommitPendingNavigation(); 4178 EXPECT_EQ(5, controller.GetCurrentEntryIndex()); 4179 for (int i = 0; i < controller.GetEntryCount() - 1; ++i) { 4180 entry = NavigationEntryImpl::FromNavigationEntry( 4181 controller.GetEntryAtIndex(i)); 4182 screenshot_manager->TakeScreenshotFor(entry); 4183 } 4184 4185 for (int i = 10; i <= 12; ++i) { 4186 entry = NavigationEntryImpl::FromNavigationEntry( 4187 controller.GetEntryAtIndex(i)); 4188 EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i 4189 << " not purged"; 4190 screenshot_manager->TakeScreenshotFor(entry); 4191 } 4192 4193 // Navigate to index 7 and assign screenshot to all entries. 4194 controller.GoToIndex(7); 4195 contents()->CommitPendingNavigation(); 4196 EXPECT_EQ(7, controller.GetCurrentEntryIndex()); 4197 for (int i = 0; i < controller.GetEntryCount() - 1; ++i) { 4198 entry = NavigationEntryImpl::FromNavigationEntry( 4199 controller.GetEntryAtIndex(i)); 4200 screenshot_manager->TakeScreenshotFor(entry); 4201 } 4202 4203 for (int i = 0; i < 2; ++i) { 4204 entry = NavigationEntryImpl::FromNavigationEntry( 4205 controller.GetEntryAtIndex(i)); 4206 EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i 4207 << " not purged"; 4208 } 4209 4210 // Clear all screenshots. 4211 EXPECT_EQ(13, controller.GetEntryCount()); 4212 EXPECT_EQ(10, screenshot_manager->GetScreenshotCount()); 4213 controller.ClearAllScreenshots(); 4214 EXPECT_EQ(0, screenshot_manager->GetScreenshotCount()); 4215 for (int i = 0; i < controller.GetEntryCount(); ++i) { 4216 entry = NavigationEntryImpl::FromNavigationEntry( 4217 controller.GetEntryAtIndex(i)); 4218 EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i 4219 << " not cleared"; 4220 } 4221 } 4222 4223 TEST_F(NavigationControllerTest, PushStateUpdatesTitle) { 4224 4225 // Navigate 4226 test_rvh()->SendNavigate(1, GURL("http://foo")); 4227 4228 // Set title 4229 base::string16 title(base::ASCIIToUTF16("Title")); 4230 controller().GetLastCommittedEntry()->SetTitle(title); 4231 4232 // history.pushState() is called. 4233 FrameHostMsg_DidCommitProvisionalLoad_Params params; 4234 GURL url("http://foo#foo"); 4235 params.page_id = 2; 4236 params.url = url; 4237 params.page_state = PageState::CreateFromURL(url); 4238 params.was_within_same_page = true; 4239 test_rvh()->SendNavigateWithParams(¶ms); 4240 4241 // The title should immediately be visible on the new NavigationEntry. 4242 base::string16 new_title = 4243 controller().GetLastCommittedEntry()->GetTitleForDisplay(std::string()); 4244 EXPECT_EQ(title, new_title); 4245 } 4246 4247 // Test that the navigation controller clears its session history when a 4248 // navigation commits with the clear history list flag set. 4249 TEST_F(NavigationControllerTest, ClearHistoryList) { 4250 const GURL url1("http://foo1"); 4251 const GURL url2("http://foo2"); 4252 const GURL url3("http://foo3"); 4253 const GURL url4("http://foo4"); 4254 4255 NavigationControllerImpl& controller = controller_impl(); 4256 4257 // Create a session history with three entries, second entry is active. 4258 NavigateAndCommit(url1); 4259 NavigateAndCommit(url2); 4260 NavigateAndCommit(url3); 4261 controller.GoBack(); 4262 contents()->CommitPendingNavigation(); 4263 EXPECT_EQ(3, controller.GetEntryCount()); 4264 EXPECT_EQ(1, controller.GetCurrentEntryIndex()); 4265 4266 // Create a new pending navigation, and indicate that the session history 4267 // should be cleared. 4268 NavigationController::LoadURLParams params(url4); 4269 params.should_clear_history_list = true; 4270 controller.LoadURLWithParams(params); 4271 4272 // Verify that the pending entry correctly indicates that the session history 4273 // should be cleared. 4274 NavigationEntryImpl* entry = 4275 NavigationEntryImpl::FromNavigationEntry( 4276 controller.GetPendingEntry()); 4277 ASSERT_TRUE(entry); 4278 EXPECT_TRUE(entry->should_clear_history_list()); 4279 4280 // Assume that the RV correctly cleared its history and commit the navigation. 4281 static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost())-> 4282 set_simulate_history_list_was_cleared(true); 4283 contents()->CommitPendingNavigation(); 4284 4285 // Verify that the NavigationController's session history was correctly 4286 // cleared. 4287 EXPECT_EQ(1, controller.GetEntryCount()); 4288 EXPECT_EQ(0, controller.GetCurrentEntryIndex()); 4289 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex()); 4290 EXPECT_EQ(-1, controller.GetPendingEntryIndex()); 4291 EXPECT_FALSE(controller.CanGoBack()); 4292 EXPECT_FALSE(controller.CanGoForward()); 4293 EXPECT_EQ(url4, controller.GetVisibleEntry()->GetURL()); 4294 } 4295 4296 TEST_F(NavigationControllerTest, PostThenReplaceStateThenReload) { 4297 scoped_ptr<TestWebContentsDelegate> delegate(new TestWebContentsDelegate()); 4298 EXPECT_FALSE(contents()->GetDelegate()); 4299 contents()->SetDelegate(delegate.get()); 4300 4301 // Submit a form. 4302 GURL url("http://foo"); 4303 FrameHostMsg_DidCommitProvisionalLoad_Params params; 4304 params.page_id = 1; 4305 params.url = url; 4306 params.transition = PAGE_TRANSITION_FORM_SUBMIT; 4307 params.gesture = NavigationGestureUser; 4308 params.page_state = PageState::CreateFromURL(url); 4309 params.was_within_same_page = false; 4310 params.is_post = true; 4311 params.post_id = 2; 4312 test_rvh()->SendNavigateWithParams(¶ms); 4313 4314 // history.replaceState() is called. 4315 GURL replace_url("http://foo#foo"); 4316 params.page_id = 1; 4317 params.url = replace_url; 4318 params.transition = PAGE_TRANSITION_LINK; 4319 params.gesture = NavigationGestureUser; 4320 params.page_state = PageState::CreateFromURL(replace_url); 4321 params.was_within_same_page = true; 4322 params.is_post = false; 4323 params.post_id = -1; 4324 test_rvh()->SendNavigateWithParams(¶ms); 4325 4326 // Now reload. replaceState overrides the POST, so we should not show a 4327 // repost warning dialog. 4328 controller_impl().Reload(true); 4329 EXPECT_EQ(0, delegate->repost_form_warning_count()); 4330 } 4331 4332 } // namespace content 4333