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