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