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