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