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