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