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