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