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