web_contents_impl_unittest.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
1// Copyright (c) 2012 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/logging.h" 6#include "base/strings/utf_string_conversions.h" 7#include "content/browser/renderer_host/render_view_host_impl.h" 8#include "content/browser/renderer_host/test_render_view_host.h" 9#include "content/browser/site_instance_impl.h" 10#include "content/browser/web_contents/frame_tree_node.h" 11#include "content/browser/web_contents/interstitial_page_impl.h" 12#include "content/browser/web_contents/navigation_entry_impl.h" 13#include "content/browser/webui/web_ui_controller_factory_registry.h" 14#include "content/common/view_messages.h" 15#include "content/public/browser/global_request_id.h" 16#include "content/public/browser/interstitial_page_delegate.h" 17#include "content/public/browser/navigation_details.h" 18#include "content/public/browser/notification_details.h" 19#include "content/public/browser/notification_source.h" 20#include "content/public/browser/notification_source.h" 21#include "content/public/browser/render_widget_host_view.h" 22#include "content/public/browser/web_contents_observer.h" 23#include "content/public/browser/web_ui_controller.h" 24#include "content/public/common/bindings_policy.h" 25#include "content/public/common/content_constants.h" 26#include "content/public/common/url_constants.h" 27#include "content/public/test/mock_render_process_host.h" 28#include "content/public/test/test_browser_thread.h" 29#include "content/public/test/test_utils.h" 30#include "content/test/test_content_browser_client.h" 31#include "content/test/test_content_client.h" 32#include "content/test/test_web_contents.h" 33#include "testing/gtest/include/gtest/gtest.h" 34 35namespace content { 36namespace { 37 38const char kTestWebUIUrl[] = "chrome://blah"; 39 40class WebContentsImplTestWebUIControllerFactory 41 : public WebUIControllerFactory { 42 public: 43 virtual WebUIController* CreateWebUIControllerForURL( 44 WebUI* web_ui, const GURL& url) const OVERRIDE { 45 if (!UseWebUI(url)) 46 return NULL; 47 48 return new WebUIController(web_ui); 49 } 50 51 virtual WebUI::TypeID GetWebUIType(BrowserContext* browser_context, 52 const GURL& url) const OVERRIDE { 53 return WebUI::kNoWebUI; 54 } 55 56 virtual bool UseWebUIForURL(BrowserContext* browser_context, 57 const GURL& url) const OVERRIDE { 58 return UseWebUI(url); 59 } 60 61 virtual bool UseWebUIBindingsForURL(BrowserContext* browser_context, 62 const GURL& url) const OVERRIDE { 63 return UseWebUI(url); 64 } 65 66 private: 67 bool UseWebUI(const GURL& url) const { 68 return url == GURL(kTestWebUIUrl); 69 } 70}; 71 72class TestInterstitialPage; 73 74class TestInterstitialPageDelegate : public InterstitialPageDelegate { 75 public: 76 TestInterstitialPageDelegate(TestInterstitialPage* interstitial_page) 77 : interstitial_page_(interstitial_page) {} 78 virtual void CommandReceived(const std::string& command) OVERRIDE; 79 virtual std::string GetHTMLContents() OVERRIDE { return std::string(); } 80 virtual void OnDontProceed() OVERRIDE; 81 virtual void OnProceed() OVERRIDE; 82 private: 83 TestInterstitialPage* interstitial_page_; 84}; 85 86class TestInterstitialPage : public InterstitialPageImpl { 87 public: 88 enum InterstitialState { 89 INVALID = 0, // Hasn't yet been initialized. 90 UNDECIDED, // Initialized, but no decision taken yet. 91 OKED, // Proceed was called. 92 CANCELED // DontProceed was called. 93 }; 94 95 class Delegate { 96 public: 97 virtual void TestInterstitialPageDeleted( 98 TestInterstitialPage* interstitial) = 0; 99 100 protected: 101 virtual ~Delegate() {} 102 }; 103 104 // IMPORTANT NOTE: if you pass stack allocated values for |state| and 105 // |deleted| (like all interstitial related tests do at this point), make sure 106 // to create an instance of the TestInterstitialPageStateGuard class on the 107 // stack in your test. This will ensure that the TestInterstitialPage states 108 // are cleared when the test finishes. 109 // Not doing so will cause stack trashing if your test does not hide the 110 // interstitial, as in such a case it will be destroyed in the test TearDown 111 // method and will dereference the |deleted| local variable which by then is 112 // out of scope. 113 TestInterstitialPage(WebContentsImpl* contents, 114 bool new_navigation, 115 const GURL& url, 116 InterstitialState* state, 117 bool* deleted) 118 : InterstitialPageImpl( 119 contents, new_navigation, url, 120 new TestInterstitialPageDelegate(this)), 121 state_(state), 122 deleted_(deleted), 123 command_received_count_(0), 124 delegate_(NULL) { 125 *state_ = UNDECIDED; 126 *deleted_ = false; 127 } 128 129 virtual ~TestInterstitialPage() { 130 if (deleted_) 131 *deleted_ = true; 132 if (delegate_) 133 delegate_->TestInterstitialPageDeleted(this); 134 } 135 136 void OnDontProceed() { 137 if (state_) 138 *state_ = CANCELED; 139 } 140 void OnProceed() { 141 if (state_) 142 *state_ = OKED; 143 } 144 145 int command_received_count() const { 146 return command_received_count_; 147 } 148 149 void TestDomOperationResponse(const std::string& json_string) { 150 if (enabled()) 151 CommandReceived(); 152 } 153 154 void TestDidNavigate(int page_id, const GURL& url) { 155 ViewHostMsg_FrameNavigate_Params params; 156 InitNavigateParams(¶ms, page_id, url, PAGE_TRANSITION_TYPED); 157 DidNavigate(GetRenderViewHostForTesting(), params); 158 } 159 160 void TestRenderViewTerminated(base::TerminationStatus status, 161 int error_code) { 162 RenderViewTerminated(GetRenderViewHostForTesting(), status, error_code); 163 } 164 165 bool is_showing() const { 166 return static_cast<TestRenderWidgetHostView*>( 167 GetRenderViewHostForTesting()->GetView())->is_showing(); 168 } 169 170 void ClearStates() { 171 state_ = NULL; 172 deleted_ = NULL; 173 delegate_ = NULL; 174 } 175 176 void CommandReceived() { 177 command_received_count_++; 178 } 179 180 void set_delegate(Delegate* delegate) { 181 delegate_ = delegate; 182 } 183 184 protected: 185 virtual RenderViewHost* CreateRenderViewHost() OVERRIDE { 186 return new TestRenderViewHost( 187 SiteInstance::Create(web_contents()->GetBrowserContext()), 188 this, this, MSG_ROUTING_NONE, MSG_ROUTING_NONE, false); 189 } 190 191 virtual WebContentsView* CreateWebContentsView() OVERRIDE { 192 return NULL; 193 } 194 195 private: 196 InterstitialState* state_; 197 bool* deleted_; 198 int command_received_count_; 199 Delegate* delegate_; 200}; 201 202void TestInterstitialPageDelegate::CommandReceived(const std::string& command) { 203 interstitial_page_->CommandReceived(); 204} 205 206void TestInterstitialPageDelegate::OnDontProceed() { 207 interstitial_page_->OnDontProceed(); 208} 209 210void TestInterstitialPageDelegate::OnProceed() { 211 interstitial_page_->OnProceed(); 212} 213 214class TestInterstitialPageStateGuard : public TestInterstitialPage::Delegate { 215 public: 216 explicit TestInterstitialPageStateGuard( 217 TestInterstitialPage* interstitial_page) 218 : interstitial_page_(interstitial_page) { 219 DCHECK(interstitial_page_); 220 interstitial_page_->set_delegate(this); 221 } 222 virtual ~TestInterstitialPageStateGuard() { 223 if (interstitial_page_) 224 interstitial_page_->ClearStates(); 225 } 226 227 virtual void TestInterstitialPageDeleted( 228 TestInterstitialPage* interstitial) OVERRIDE { 229 DCHECK(interstitial_page_ == interstitial); 230 interstitial_page_ = NULL; 231 } 232 233 private: 234 TestInterstitialPage* interstitial_page_; 235}; 236 237class WebContentsImplTest : public RenderViewHostImplTestHarness { 238 public: 239 virtual void SetUp() { 240 RenderViewHostImplTestHarness::SetUp(); 241 WebUIControllerFactory::RegisterFactory(&factory_); 242 } 243 244 virtual void TearDown() { 245 WebUIControllerFactory::UnregisterFactoryForTesting(&factory_); 246 RenderViewHostImplTestHarness::TearDown(); 247 } 248 249 private: 250 WebContentsImplTestWebUIControllerFactory factory_; 251}; 252 253class TestWebContentsObserver : public WebContentsObserver { 254 public: 255 TestWebContentsObserver(WebContents* contents) 256 : WebContentsObserver(contents) { 257 } 258 virtual ~TestWebContentsObserver() {} 259 260 virtual void DidFinishLoad(int64 frame_id, 261 const GURL& validated_url, 262 bool is_main_frame, 263 RenderViewHost* render_view_host) OVERRIDE { 264 last_url_ = validated_url; 265 } 266 virtual void DidFailLoad(int64 frame_id, 267 const GURL& validated_url, 268 bool is_main_frame, 269 int error_code, 270 const string16& error_description, 271 RenderViewHost* render_view_host) OVERRIDE { 272 last_url_ = validated_url; 273 } 274 275 const GURL& last_url() const { return last_url_; } 276 277 private: 278 GURL last_url_; 279 280 DISALLOW_COPY_AND_ASSIGN(TestWebContentsObserver); 281}; 282 283} // namespace 284 285// Test to make sure that title updates get stripped of whitespace. 286TEST_F(WebContentsImplTest, UpdateTitle) { 287 NavigationControllerImpl& cont = 288 static_cast<NavigationControllerImpl&>(controller()); 289 ViewHostMsg_FrameNavigate_Params params; 290 InitNavigateParams(¶ms, 0, GURL(kAboutBlankURL), PAGE_TRANSITION_TYPED); 291 292 LoadCommittedDetails details; 293 cont.RendererDidNavigate(params, &details); 294 295 contents()->UpdateTitle(rvh(), 0, ASCIIToUTF16(" Lots O' Whitespace\n"), 296 base::i18n::LEFT_TO_RIGHT); 297 EXPECT_EQ(ASCIIToUTF16("Lots O' Whitespace"), contents()->GetTitle()); 298} 299 300// Test view source mode for a webui page. 301TEST_F(WebContentsImplTest, NTPViewSource) { 302 NavigationControllerImpl& cont = 303 static_cast<NavigationControllerImpl&>(controller()); 304 const char kUrl[] = "view-source:chrome://blah"; 305 const GURL kGURL(kUrl); 306 307 process()->sink().ClearMessages(); 308 309 cont.LoadURL( 310 kGURL, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 311 rvh()->GetDelegate()->RenderViewCreated(rvh()); 312 // Did we get the expected message? 313 EXPECT_TRUE(process()->sink().GetFirstMessageMatching( 314 ViewMsg_EnableViewSourceMode::ID)); 315 316 ViewHostMsg_FrameNavigate_Params params; 317 InitNavigateParams(¶ms, 0, kGURL, PAGE_TRANSITION_TYPED); 318 LoadCommittedDetails details; 319 cont.RendererDidNavigate(params, &details); 320 // Also check title and url. 321 EXPECT_EQ(ASCIIToUTF16(kUrl), contents()->GetTitle()); 322} 323 324// Test to ensure UpdateMaxPageID is working properly. 325TEST_F(WebContentsImplTest, UpdateMaxPageID) { 326 SiteInstance* instance1 = contents()->GetSiteInstance(); 327 scoped_refptr<SiteInstance> instance2(SiteInstance::Create(NULL)); 328 329 // Starts at -1. 330 EXPECT_EQ(-1, contents()->GetMaxPageID()); 331 EXPECT_EQ(-1, contents()->GetMaxPageIDForSiteInstance(instance1)); 332 EXPECT_EQ(-1, contents()->GetMaxPageIDForSiteInstance(instance2.get())); 333 334 // Make sure max_page_id_ is monotonically increasing per SiteInstance. 335 contents()->UpdateMaxPageID(3); 336 contents()->UpdateMaxPageID(1); 337 EXPECT_EQ(3, contents()->GetMaxPageID()); 338 EXPECT_EQ(3, contents()->GetMaxPageIDForSiteInstance(instance1)); 339 EXPECT_EQ(-1, contents()->GetMaxPageIDForSiteInstance(instance2.get())); 340 341 contents()->UpdateMaxPageIDForSiteInstance(instance2.get(), 7); 342 EXPECT_EQ(3, contents()->GetMaxPageID()); 343 EXPECT_EQ(3, contents()->GetMaxPageIDForSiteInstance(instance1)); 344 EXPECT_EQ(7, contents()->GetMaxPageIDForSiteInstance(instance2.get())); 345} 346 347// Test simple same-SiteInstance navigation. 348TEST_F(WebContentsImplTest, SimpleNavigation) { 349 TestRenderViewHost* orig_rvh = test_rvh(); 350 SiteInstance* instance1 = contents()->GetSiteInstance(); 351 EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); 352 353 // Navigate to URL 354 const GURL url("http://www.google.com"); 355 controller().LoadURL( 356 url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 357 EXPECT_FALSE(contents()->cross_navigation_pending()); 358 EXPECT_EQ(instance1, orig_rvh->GetSiteInstance()); 359 // Controller's pending entry will have a NULL site instance until we assign 360 // it in DidNavigate. 361 EXPECT_TRUE( 362 NavigationEntryImpl::FromNavigationEntry(controller().GetActiveEntry())-> 363 site_instance() == NULL); 364 365 // DidNavigate from the page 366 contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); 367 EXPECT_FALSE(contents()->cross_navigation_pending()); 368 EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); 369 EXPECT_EQ(instance1, orig_rvh->GetSiteInstance()); 370 // Controller's entry should now have the SiteInstance, or else we won't be 371 // able to find it later. 372 EXPECT_EQ( 373 instance1, 374 NavigationEntryImpl::FromNavigationEntry(controller().GetActiveEntry())-> 375 site_instance()); 376} 377 378// Test that we reject NavigateToEntry if the url is over kMaxURLChars. 379TEST_F(WebContentsImplTest, NavigateToExcessivelyLongURL) { 380 // Construct a URL that's kMaxURLChars + 1 long of all 'a's. 381 const GURL url(std::string("http://example.org/").append( 382 kMaxURLChars + 1, 'a')); 383 384 controller().LoadURL( 385 url, Referrer(), PAGE_TRANSITION_GENERATED, std::string()); 386 EXPECT_TRUE(controller().GetActiveEntry() == NULL); 387} 388 389// Test that navigating across a site boundary creates a new RenderViewHost 390// with a new SiteInstance. Going back should do the same. 391TEST_F(WebContentsImplTest, CrossSiteBoundaries) { 392 contents()->transition_cross_site = true; 393 TestRenderViewHost* orig_rvh = test_rvh(); 394 int orig_rvh_delete_count = 0; 395 orig_rvh->set_delete_counter(&orig_rvh_delete_count); 396 SiteInstance* instance1 = contents()->GetSiteInstance(); 397 398 // Navigate to URL. First URL should use first RenderViewHost. 399 const GURL url("http://www.google.com"); 400 controller().LoadURL( 401 url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 402 contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); 403 404 EXPECT_FALSE(contents()->cross_navigation_pending()); 405 EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); 406 EXPECT_EQ(url, contents()->GetLastCommittedURL()); 407 EXPECT_EQ(url, contents()->GetActiveURL()); 408 409 // Navigate to new site 410 const GURL url2("http://www.yahoo.com"); 411 controller().LoadURL( 412 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 413 EXPECT_TRUE(contents()->cross_navigation_pending()); 414 EXPECT_EQ(url, contents()->GetLastCommittedURL()); 415 EXPECT_EQ(url2, contents()->GetActiveURL()); 416 TestRenderViewHost* pending_rvh = 417 static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost()); 418 int pending_rvh_delete_count = 0; 419 pending_rvh->set_delete_counter(&pending_rvh_delete_count); 420 421 // Navigations should be suspended in pending_rvh until ShouldCloseACK. 422 EXPECT_TRUE(pending_rvh->are_navigations_suspended()); 423 orig_rvh->SendShouldCloseACK(true); 424 EXPECT_FALSE(pending_rvh->are_navigations_suspended()); 425 426 // DidNavigate from the pending page 427 contents()->TestDidNavigate( 428 pending_rvh, 1, url2, PAGE_TRANSITION_TYPED); 429 SiteInstance* instance2 = contents()->GetSiteInstance(); 430 431 EXPECT_FALSE(contents()->cross_navigation_pending()); 432 EXPECT_EQ(pending_rvh, contents()->GetRenderViewHost()); 433 EXPECT_EQ(url2, contents()->GetLastCommittedURL()); 434 EXPECT_EQ(url2, contents()->GetActiveURL()); 435 EXPECT_NE(instance1, instance2); 436 EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); 437 // We keep the original RVH around, swapped out. 438 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->IsOnSwappedOutList( 439 orig_rvh)); 440 EXPECT_EQ(orig_rvh_delete_count, 0); 441 442 // Going back should switch SiteInstances again. The first SiteInstance is 443 // stored in the NavigationEntry, so it should be the same as at the start. 444 // We should use the same RVH as before, swapping it back in. 445 controller().GoBack(); 446 TestRenderViewHost* goback_rvh = 447 static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost()); 448 EXPECT_EQ(orig_rvh, goback_rvh); 449 EXPECT_TRUE(contents()->cross_navigation_pending()); 450 451 // Navigations should be suspended in goback_rvh until ShouldCloseACK. 452 EXPECT_TRUE(goback_rvh->are_navigations_suspended()); 453 pending_rvh->SendShouldCloseACK(true); 454 EXPECT_FALSE(goback_rvh->are_navigations_suspended()); 455 456 // DidNavigate from the back action 457 contents()->TestDidNavigate( 458 goback_rvh, 1, url2, PAGE_TRANSITION_TYPED); 459 EXPECT_FALSE(contents()->cross_navigation_pending()); 460 EXPECT_EQ(goback_rvh, contents()->GetRenderViewHost()); 461 EXPECT_EQ(instance1, contents()->GetSiteInstance()); 462 // The pending RVH should now be swapped out, not deleted. 463 EXPECT_TRUE(contents()->GetRenderManagerForTesting()-> 464 IsOnSwappedOutList(pending_rvh)); 465 EXPECT_EQ(pending_rvh_delete_count, 0); 466 467 // Close contents and ensure RVHs are deleted. 468 DeleteContents(); 469 EXPECT_EQ(orig_rvh_delete_count, 1); 470 EXPECT_EQ(pending_rvh_delete_count, 1); 471} 472 473// Test that navigating across a site boundary after a crash creates a new 474// RVH without requiring a cross-site transition (i.e., PENDING state). 475TEST_F(WebContentsImplTest, CrossSiteBoundariesAfterCrash) { 476 contents()->transition_cross_site = true; 477 TestRenderViewHost* orig_rvh = test_rvh(); 478 int orig_rvh_delete_count = 0; 479 orig_rvh->set_delete_counter(&orig_rvh_delete_count); 480 SiteInstance* instance1 = contents()->GetSiteInstance(); 481 482 // Navigate to URL. First URL should use first RenderViewHost. 483 const GURL url("http://www.google.com"); 484 controller().LoadURL( 485 url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 486 contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); 487 488 EXPECT_FALSE(contents()->cross_navigation_pending()); 489 EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); 490 491 // Crash the renderer. 492 orig_rvh->set_render_view_created(false); 493 494 // Navigate to new site. We should not go into PENDING. 495 const GURL url2("http://www.yahoo.com"); 496 controller().LoadURL( 497 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 498 RenderViewHost* new_rvh = rvh(); 499 EXPECT_FALSE(contents()->cross_navigation_pending()); 500 EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); 501 EXPECT_NE(orig_rvh, new_rvh); 502 EXPECT_EQ(orig_rvh_delete_count, 1); 503 504 // DidNavigate from the new page 505 contents()->TestDidNavigate(new_rvh, 1, url2, PAGE_TRANSITION_TYPED); 506 SiteInstance* instance2 = contents()->GetSiteInstance(); 507 508 EXPECT_FALSE(contents()->cross_navigation_pending()); 509 EXPECT_EQ(new_rvh, rvh()); 510 EXPECT_NE(instance1, instance2); 511 EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); 512 513 // Close contents and ensure RVHs are deleted. 514 DeleteContents(); 515 EXPECT_EQ(orig_rvh_delete_count, 1); 516} 517 518// Test that opening a new contents in the same SiteInstance and then navigating 519// both contentses to a new site will place both contentses in a single 520// SiteInstance. 521TEST_F(WebContentsImplTest, NavigateTwoTabsCrossSite) { 522 contents()->transition_cross_site = true; 523 TestRenderViewHost* orig_rvh = test_rvh(); 524 SiteInstance* instance1 = contents()->GetSiteInstance(); 525 526 // Navigate to URL. First URL should use first RenderViewHost. 527 const GURL url("http://www.google.com"); 528 controller().LoadURL( 529 url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 530 contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); 531 532 // Open a new contents with the same SiteInstance, navigated to the same site. 533 scoped_ptr<TestWebContents> contents2( 534 TestWebContents::Create(browser_context_.get(), instance1)); 535 contents2->transition_cross_site = true; 536 contents2->GetController().LoadURL(url, Referrer(), 537 PAGE_TRANSITION_TYPED, 538 std::string()); 539 // Need this page id to be 2 since the site instance is the same (which is the 540 // scope of page IDs) and we want to consider this a new page. 541 contents2->TestDidNavigate( 542 contents2->GetRenderViewHost(), 2, url, PAGE_TRANSITION_TYPED); 543 544 // Navigate first contents to a new site. 545 const GURL url2a("http://www.yahoo.com"); 546 controller().LoadURL( 547 url2a, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 548 orig_rvh->SendShouldCloseACK(true); 549 TestRenderViewHost* pending_rvh_a = 550 static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost()); 551 contents()->TestDidNavigate( 552 pending_rvh_a, 1, url2a, PAGE_TRANSITION_TYPED); 553 SiteInstance* instance2a = contents()->GetSiteInstance(); 554 EXPECT_NE(instance1, instance2a); 555 556 // Navigate second contents to the same site as the first tab. 557 const GURL url2b("http://mail.yahoo.com"); 558 contents2->GetController().LoadURL(url2b, Referrer(), 559 PAGE_TRANSITION_TYPED, 560 std::string()); 561 TestRenderViewHost* rvh2 = 562 static_cast<TestRenderViewHost*>(contents2->GetRenderViewHost()); 563 rvh2->SendShouldCloseACK(true); 564 TestRenderViewHost* pending_rvh_b = 565 static_cast<TestRenderViewHost*>(contents2->GetPendingRenderViewHost()); 566 EXPECT_TRUE(pending_rvh_b != NULL); 567 EXPECT_TRUE(contents2->cross_navigation_pending()); 568 569 // NOTE(creis): We used to be in danger of showing a crash page here if the 570 // second contents hadn't navigated somewhere first (bug 1145430). That case 571 // is now covered by the CrossSiteBoundariesAfterCrash test. 572 contents2->TestDidNavigate( 573 pending_rvh_b, 2, url2b, PAGE_TRANSITION_TYPED); 574 SiteInstance* instance2b = contents2->GetSiteInstance(); 575 EXPECT_NE(instance1, instance2b); 576 577 // Both contentses should now be in the same SiteInstance. 578 EXPECT_EQ(instance2a, instance2b); 579} 580 581TEST_F(WebContentsImplTest, NavigateFromChromeNativeKeepsSiteInstance) { 582 contents()->transition_cross_site = true; 583 TestRenderViewHost* orig_rvh = test_rvh(); 584 int orig_rvh_delete_count = 0; 585 orig_rvh->set_delete_counter(&orig_rvh_delete_count); 586 SiteInstanceImpl* orig_instance = 587 static_cast<SiteInstanceImpl*>(contents()->GetSiteInstance()); 588 589 // Navigate to a chrome-native URL. 590 const GURL native_url("chrome-native://nativestuffandthings"); 591 controller().LoadURL( 592 native_url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 593 contents()->TestDidNavigate(orig_rvh, 1, native_url, PAGE_TRANSITION_TYPED); 594 595 EXPECT_FALSE(contents()->cross_navigation_pending()); 596 EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); 597 EXPECT_EQ(native_url, contents()->GetLastCommittedURL()); 598 EXPECT_EQ(native_url, contents()->GetActiveURL()); 599 EXPECT_EQ(orig_instance, contents()->GetSiteInstance()); 600 EXPECT_EQ(GURL(), contents()->GetSiteInstance()->GetSiteURL()); 601 EXPECT_FALSE(orig_instance->HasSite()); 602 603 // Navigate to new site (should keep same site instance). 604 const GURL url("http://www.google.com"); 605 controller().LoadURL( 606 url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 607 EXPECT_FALSE(contents()->cross_navigation_pending()); 608 EXPECT_EQ(native_url, contents()->GetLastCommittedURL()); 609 EXPECT_EQ(url, contents()->GetActiveURL()); 610 EXPECT_FALSE(contents()->GetPendingRenderViewHost()); 611 contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); 612 EXPECT_EQ(orig_instance, contents()->GetSiteInstance()); 613 EXPECT_TRUE( 614 contents()->GetSiteInstance()->GetSiteURL().DomainIs("google.com")); 615 EXPECT_EQ(url, contents()->GetLastCommittedURL()); 616 617 // Navigate to another new site (should create a new site instance). 618 const GURL url2("http://www.yahoo.com"); 619 controller().LoadURL( 620 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 621 EXPECT_TRUE(contents()->cross_navigation_pending()); 622 EXPECT_EQ(url, contents()->GetLastCommittedURL()); 623 EXPECT_EQ(url2, contents()->GetActiveURL()); 624 TestRenderViewHost* pending_rvh = 625 static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost()); 626 int pending_rvh_delete_count = 0; 627 pending_rvh->set_delete_counter(&pending_rvh_delete_count); 628 629 // Navigations should be suspended in pending_rvh until ShouldCloseACK. 630 EXPECT_TRUE(pending_rvh->are_navigations_suspended()); 631 orig_rvh->SendShouldCloseACK(true); 632 EXPECT_FALSE(pending_rvh->are_navigations_suspended()); 633 634 // DidNavigate from the pending page. 635 contents()->TestDidNavigate( 636 pending_rvh, 1, url2, PAGE_TRANSITION_TYPED); 637 SiteInstance* new_instance = contents()->GetSiteInstance(); 638 639 EXPECT_FALSE(contents()->cross_navigation_pending()); 640 EXPECT_EQ(pending_rvh, contents()->GetRenderViewHost()); 641 EXPECT_EQ(url2, contents()->GetLastCommittedURL()); 642 EXPECT_EQ(url2, contents()->GetActiveURL()); 643 EXPECT_NE(new_instance, orig_instance); 644 EXPECT_FALSE(contents()->GetPendingRenderViewHost()); 645 // We keep the original RVH around, swapped out. 646 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->IsOnSwappedOutList( 647 orig_rvh)); 648 EXPECT_EQ(orig_rvh_delete_count, 0); 649 650 // Close contents and ensure RVHs are deleted. 651 DeleteContents(); 652 EXPECT_EQ(orig_rvh_delete_count, 1); 653 EXPECT_EQ(pending_rvh_delete_count, 1); 654} 655 656// Test that we can find an opener RVH even if it's pending. 657// http://crbug.com/176252. 658TEST_F(WebContentsImplTest, FindOpenerRVHWhenPending) { 659 contents()->transition_cross_site = true; 660 TestRenderViewHost* orig_rvh = test_rvh(); 661 662 // Navigate to a URL. 663 const GURL url("http://www.google.com"); 664 controller().LoadURL( 665 url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 666 contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); 667 668 // Start to navigate first tab to a new site, so that it has a pending RVH. 669 const GURL url2("http://www.yahoo.com"); 670 controller().LoadURL( 671 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 672 orig_rvh->SendShouldCloseACK(true); 673 TestRenderViewHost* pending_rvh = 674 static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost()); 675 676 // While it is still pending, simulate opening a new tab with the first tab 677 // as its opener. This will call WebContentsImpl::CreateOpenerRenderViews 678 // on the opener to ensure that an RVH exists. 679 int opener_routing_id = contents()->CreateOpenerRenderViews( 680 pending_rvh->GetSiteInstance()); 681 682 // We should find the pending RVH and not create a new one. 683 EXPECT_EQ(pending_rvh->GetRoutingID(), opener_routing_id); 684} 685 686// Tests that WebContentsImpl uses the current URL, not the SiteInstance's site, 687// to determine whether a navigation is cross-site. 688TEST_F(WebContentsImplTest, CrossSiteComparesAgainstCurrentPage) { 689 contents()->transition_cross_site = true; 690 RenderViewHost* orig_rvh = rvh(); 691 SiteInstance* instance1 = contents()->GetSiteInstance(); 692 693 // Navigate to URL. 694 const GURL url("http://www.google.com"); 695 controller().LoadURL( 696 url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 697 contents()->TestDidNavigate( 698 orig_rvh, 1, url, PAGE_TRANSITION_TYPED); 699 700 // Open a related contents to a second site. 701 scoped_ptr<TestWebContents> contents2( 702 TestWebContents::Create(browser_context_.get(), instance1)); 703 contents2->transition_cross_site = true; 704 const GURL url2("http://www.yahoo.com"); 705 contents2->GetController().LoadURL(url2, Referrer(), 706 PAGE_TRANSITION_TYPED, 707 std::string()); 708 // The first RVH in contents2 isn't live yet, so we shortcut the cross site 709 // pending. 710 TestRenderViewHost* rvh2 = static_cast<TestRenderViewHost*>( 711 contents2->GetRenderViewHost()); 712 EXPECT_FALSE(contents2->cross_navigation_pending()); 713 contents2->TestDidNavigate(rvh2, 2, url2, PAGE_TRANSITION_TYPED); 714 SiteInstance* instance2 = contents2->GetSiteInstance(); 715 EXPECT_NE(instance1, instance2); 716 EXPECT_FALSE(contents2->cross_navigation_pending()); 717 718 // Simulate a link click in first contents to second site. Doesn't switch 719 // SiteInstances, because we don't intercept WebKit navigations. 720 contents()->TestDidNavigate( 721 orig_rvh, 2, url2, PAGE_TRANSITION_TYPED); 722 SiteInstance* instance3 = contents()->GetSiteInstance(); 723 EXPECT_EQ(instance1, instance3); 724 EXPECT_FALSE(contents()->cross_navigation_pending()); 725 726 // Navigate to the new site. Doesn't switch SiteInstancees, because we 727 // compare against the current URL, not the SiteInstance's site. 728 const GURL url3("http://mail.yahoo.com"); 729 controller().LoadURL( 730 url3, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 731 EXPECT_FALSE(contents()->cross_navigation_pending()); 732 contents()->TestDidNavigate( 733 orig_rvh, 3, url3, PAGE_TRANSITION_TYPED); 734 SiteInstance* instance4 = contents()->GetSiteInstance(); 735 EXPECT_EQ(instance1, instance4); 736} 737 738// Test that the onbeforeunload and onunload handlers run when navigating 739// across site boundaries. 740TEST_F(WebContentsImplTest, CrossSiteUnloadHandlers) { 741 contents()->transition_cross_site = true; 742 TestRenderViewHost* orig_rvh = test_rvh(); 743 SiteInstance* instance1 = contents()->GetSiteInstance(); 744 745 // Navigate to URL. First URL should use first RenderViewHost. 746 const GURL url("http://www.google.com"); 747 controller().LoadURL( 748 url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 749 contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); 750 EXPECT_FALSE(contents()->cross_navigation_pending()); 751 EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); 752 753 // Navigate to new site, but simulate an onbeforeunload denial. 754 const GURL url2("http://www.yahoo.com"); 755 controller().LoadURL( 756 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 757 EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack()); 758 base::TimeTicks now = base::TimeTicks::Now(); 759 orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, false, now, now)); 760 EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack()); 761 EXPECT_FALSE(contents()->cross_navigation_pending()); 762 EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); 763 764 // Navigate again, but simulate an onbeforeunload approval. 765 controller().LoadURL( 766 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 767 EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack()); 768 now = base::TimeTicks::Now(); 769 orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now)); 770 EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack()); 771 EXPECT_TRUE(contents()->cross_navigation_pending()); 772 TestRenderViewHost* pending_rvh = static_cast<TestRenderViewHost*>( 773 contents()->GetPendingRenderViewHost()); 774 775 // We won't hear DidNavigate until the onunload handler has finished running. 776 777 // DidNavigate from the pending page. 778 contents()->TestDidNavigate( 779 pending_rvh, 1, url2, PAGE_TRANSITION_TYPED); 780 SiteInstance* instance2 = contents()->GetSiteInstance(); 781 EXPECT_FALSE(contents()->cross_navigation_pending()); 782 EXPECT_EQ(pending_rvh, rvh()); 783 EXPECT_NE(instance1, instance2); 784 EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); 785} 786 787// Test that during a slow cross-site navigation, the original renderer can 788// navigate to a different URL and have it displayed, canceling the slow 789// navigation. 790TEST_F(WebContentsImplTest, CrossSiteNavigationPreempted) { 791 contents()->transition_cross_site = true; 792 TestRenderViewHost* orig_rvh = test_rvh(); 793 SiteInstance* instance1 = contents()->GetSiteInstance(); 794 795 // Navigate to URL. First URL should use first RenderViewHost. 796 const GURL url("http://www.google.com"); 797 controller().LoadURL( 798 url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 799 contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); 800 EXPECT_FALSE(contents()->cross_navigation_pending()); 801 EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); 802 803 // Navigate to new site, simulating an onbeforeunload approval. 804 const GURL url2("http://www.yahoo.com"); 805 controller().LoadURL( 806 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 807 EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack()); 808 base::TimeTicks now = base::TimeTicks::Now(); 809 orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now)); 810 EXPECT_TRUE(contents()->cross_navigation_pending()); 811 812 // Suppose the original renderer navigates before the new one is ready. 813 orig_rvh->SendNavigate(2, GURL("http://www.google.com/foo")); 814 815 // Verify that the pending navigation is cancelled. 816 EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack()); 817 SiteInstance* instance2 = contents()->GetSiteInstance(); 818 EXPECT_FALSE(contents()->cross_navigation_pending()); 819 EXPECT_EQ(orig_rvh, rvh()); 820 EXPECT_EQ(instance1, instance2); 821 EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); 822} 823 824TEST_F(WebContentsImplTest, CrossSiteNavigationBackPreempted) { 825 contents()->transition_cross_site = true; 826 827 // Start with a web ui page, which gets a new RVH with WebUI bindings. 828 const GURL url1("chrome://blah"); 829 controller().LoadURL( 830 url1, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 831 TestRenderViewHost* ntp_rvh = test_rvh(); 832 contents()->TestDidNavigate(ntp_rvh, 1, url1, PAGE_TRANSITION_TYPED); 833 NavigationEntry* entry1 = controller().GetLastCommittedEntry(); 834 SiteInstance* instance1 = contents()->GetSiteInstance(); 835 836 EXPECT_FALSE(contents()->cross_navigation_pending()); 837 EXPECT_EQ(ntp_rvh, contents()->GetRenderViewHost()); 838 EXPECT_EQ(url1, entry1->GetURL()); 839 EXPECT_EQ(instance1, 840 NavigationEntryImpl::FromNavigationEntry(entry1)->site_instance()); 841 EXPECT_TRUE(ntp_rvh->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI); 842 843 // Navigate to new site. 844 const GURL url2("http://www.google.com"); 845 controller().LoadURL( 846 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 847 EXPECT_TRUE(contents()->cross_navigation_pending()); 848 TestRenderViewHost* google_rvh = 849 static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost()); 850 851 // Simulate beforeunload approval. 852 EXPECT_TRUE(ntp_rvh->is_waiting_for_beforeunload_ack()); 853 base::TimeTicks now = base::TimeTicks::Now(); 854 ntp_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now)); 855 856 // DidNavigate from the pending page. 857 contents()->TestDidNavigate( 858 google_rvh, 1, url2, PAGE_TRANSITION_TYPED); 859 NavigationEntry* entry2 = controller().GetLastCommittedEntry(); 860 SiteInstance* instance2 = contents()->GetSiteInstance(); 861 862 EXPECT_FALSE(contents()->cross_navigation_pending()); 863 EXPECT_EQ(google_rvh, contents()->GetRenderViewHost()); 864 EXPECT_NE(instance1, instance2); 865 EXPECT_FALSE(contents()->GetPendingRenderViewHost()); 866 EXPECT_EQ(url2, entry2->GetURL()); 867 EXPECT_EQ(instance2, 868 NavigationEntryImpl::FromNavigationEntry(entry2)->site_instance()); 869 EXPECT_FALSE(google_rvh->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI); 870 871 // Navigate to third page on same site. 872 const GURL url3("http://news.google.com"); 873 controller().LoadURL( 874 url3, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 875 EXPECT_FALSE(contents()->cross_navigation_pending()); 876 contents()->TestDidNavigate( 877 google_rvh, 2, url3, PAGE_TRANSITION_TYPED); 878 NavigationEntry* entry3 = controller().GetLastCommittedEntry(); 879 SiteInstance* instance3 = contents()->GetSiteInstance(); 880 881 EXPECT_FALSE(contents()->cross_navigation_pending()); 882 EXPECT_EQ(google_rvh, contents()->GetRenderViewHost()); 883 EXPECT_EQ(instance2, instance3); 884 EXPECT_FALSE(contents()->GetPendingRenderViewHost()); 885 EXPECT_EQ(url3, entry3->GetURL()); 886 EXPECT_EQ(instance3, 887 NavigationEntryImpl::FromNavigationEntry(entry3)->site_instance()); 888 889 // Go back within the site. 890 controller().GoBack(); 891 EXPECT_FALSE(contents()->cross_navigation_pending()); 892 EXPECT_EQ(entry2, controller().GetPendingEntry()); 893 894 // Before that commits, go back again. 895 controller().GoBack(); 896 EXPECT_TRUE(contents()->cross_navigation_pending()); 897 EXPECT_TRUE(contents()->GetPendingRenderViewHost()); 898 EXPECT_EQ(entry1, controller().GetPendingEntry()); 899 900 // Simulate beforeunload approval. 901 EXPECT_TRUE(google_rvh->is_waiting_for_beforeunload_ack()); 902 now = base::TimeTicks::Now(); 903 google_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now)); 904 905 // DidNavigate from the first back. This aborts the second back's pending RVH. 906 contents()->TestDidNavigate(google_rvh, 1, url2, PAGE_TRANSITION_TYPED); 907 908 // We should commit this page and forget about the second back. 909 EXPECT_FALSE(contents()->cross_navigation_pending()); 910 EXPECT_FALSE(controller().GetPendingEntry()); 911 EXPECT_EQ(google_rvh, contents()->GetRenderViewHost()); 912 EXPECT_EQ(url2, controller().GetLastCommittedEntry()->GetURL()); 913 914 // We should not have corrupted the NTP entry. 915 EXPECT_EQ(instance3, 916 NavigationEntryImpl::FromNavigationEntry(entry3)->site_instance()); 917 EXPECT_EQ(instance2, 918 NavigationEntryImpl::FromNavigationEntry(entry2)->site_instance()); 919 EXPECT_EQ(instance1, 920 NavigationEntryImpl::FromNavigationEntry(entry1)->site_instance()); 921 EXPECT_EQ(url1, entry1->GetURL()); 922} 923 924// Test that during a slow cross-site navigation, a sub-frame navigation in the 925// original renderer will not cancel the slow navigation (bug 42029). 926TEST_F(WebContentsImplTest, CrossSiteNavigationNotPreemptedByFrame) { 927 contents()->transition_cross_site = true; 928 TestRenderViewHost* orig_rvh = test_rvh(); 929 930 // Navigate to URL. First URL should use first RenderViewHost. 931 const GURL url("http://www.google.com"); 932 controller().LoadURL( 933 url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 934 contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); 935 EXPECT_FALSE(contents()->cross_navigation_pending()); 936 EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); 937 938 // Start navigating to new site. 939 const GURL url2("http://www.yahoo.com"); 940 controller().LoadURL( 941 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 942 943 // Simulate a sub-frame navigation arriving and ensure the RVH is still 944 // waiting for a before unload response. 945 orig_rvh->SendNavigateWithTransition(1, GURL("http://google.com/frame"), 946 PAGE_TRANSITION_AUTO_SUBFRAME); 947 EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack()); 948 949 // Now simulate the onbeforeunload approval and verify the navigation is 950 // not canceled. 951 base::TimeTicks now = base::TimeTicks::Now(); 952 orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now)); 953 EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack()); 954 EXPECT_TRUE(contents()->cross_navigation_pending()); 955} 956 957// Test that a cross-site navigation is not preempted if the previous 958// renderer sends a FrameNavigate message just before being told to stop. 959// We should only preempt the cross-site navigation if the previous renderer 960// has started a new navigation. See http://crbug.com/79176. 961TEST_F(WebContentsImplTest, CrossSiteNotPreemptedDuringBeforeUnload) { 962 contents()->transition_cross_site = true; 963 964 // Navigate to NTP URL. 965 const GURL url("chrome://blah"); 966 controller().LoadURL( 967 url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 968 TestRenderViewHost* orig_rvh = test_rvh(); 969 EXPECT_FALSE(contents()->cross_navigation_pending()); 970 971 // Navigate to new site, with the beforeunload request in flight. 972 const GURL url2("http://www.yahoo.com"); 973 controller().LoadURL( 974 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 975 TestRenderViewHost* pending_rvh = 976 static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost()); 977 EXPECT_TRUE(contents()->cross_navigation_pending()); 978 EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack()); 979 980 // Suppose the first navigation tries to commit now, with a 981 // ViewMsg_Stop in flight. This should not cancel the pending navigation, 982 // but it should act as if the beforeunload ack arrived. 983 orig_rvh->SendNavigate(1, GURL("chrome://blah")); 984 EXPECT_TRUE(contents()->cross_navigation_pending()); 985 EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); 986 EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack()); 987 988 // The pending navigation should be able to commit successfully. 989 contents()->TestDidNavigate(pending_rvh, 1, url2, PAGE_TRANSITION_TYPED); 990 EXPECT_FALSE(contents()->cross_navigation_pending()); 991 EXPECT_EQ(pending_rvh, contents()->GetRenderViewHost()); 992} 993 994// Test that the original renderer cannot preempt a cross-site navigation once 995// the unload request has been made. At this point, the cross-site navigation 996// is almost ready to be displayed, and the original renderer is only given a 997// short chance to run an unload handler. Prevents regression of bug 23942. 998TEST_F(WebContentsImplTest, CrossSiteCantPreemptAfterUnload) { 999 contents()->transition_cross_site = true; 1000 TestRenderViewHost* orig_rvh = test_rvh(); 1001 SiteInstance* instance1 = contents()->GetSiteInstance(); 1002 1003 // Navigate to URL. First URL should use first RenderViewHost. 1004 const GURL url("http://www.google.com"); 1005 controller().LoadURL( 1006 url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 1007 contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); 1008 EXPECT_FALSE(contents()->cross_navigation_pending()); 1009 EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); 1010 1011 // Navigate to new site, simulating an onbeforeunload approval. 1012 const GURL url2("http://www.yahoo.com"); 1013 controller().LoadURL( 1014 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 1015 base::TimeTicks now = base::TimeTicks::Now(); 1016 orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now)); 1017 EXPECT_TRUE(contents()->cross_navigation_pending()); 1018 TestRenderViewHost* pending_rvh = static_cast<TestRenderViewHost*>( 1019 contents()->GetPendingRenderViewHost()); 1020 1021 // Simulate the pending renderer's response, which leads to an unload request 1022 // being sent to orig_rvh. 1023 contents()->GetRenderManagerForTesting()->OnCrossSiteResponse( 1024 pending_rvh, GlobalRequestID(0, 0)); 1025 1026 // Suppose the original renderer navigates now, while the unload request is in 1027 // flight. We should ignore it, wait for the unload ack, and let the pending 1028 // request continue. Otherwise, the contents may close spontaneously or stop 1029 // responding to navigation requests. (See bug 23942.) 1030 ViewHostMsg_FrameNavigate_Params params1a; 1031 InitNavigateParams(¶ms1a, 2, GURL("http://www.google.com/foo"), 1032 PAGE_TRANSITION_TYPED); 1033 orig_rvh->SendNavigate(2, GURL("http://www.google.com/foo")); 1034 1035 // Verify that the pending navigation is still in progress. 1036 EXPECT_TRUE(contents()->cross_navigation_pending()); 1037 EXPECT_TRUE(contents()->GetPendingRenderViewHost() != NULL); 1038 1039 // DidNavigate from the pending page should commit it. 1040 contents()->TestDidNavigate( 1041 pending_rvh, 1, url2, PAGE_TRANSITION_TYPED); 1042 SiteInstance* instance2 = contents()->GetSiteInstance(); 1043 EXPECT_FALSE(contents()->cross_navigation_pending()); 1044 EXPECT_EQ(pending_rvh, rvh()); 1045 EXPECT_NE(instance1, instance2); 1046 EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); 1047} 1048 1049// Test that a cross-site navigation that doesn't commit after the unload 1050// handler doesn't leave the contents in a stuck state. http://crbug.com/88562 1051TEST_F(WebContentsImplTest, CrossSiteNavigationCanceled) { 1052 contents()->transition_cross_site = true; 1053 TestRenderViewHost* orig_rvh = test_rvh(); 1054 SiteInstance* instance1 = contents()->GetSiteInstance(); 1055 1056 // Navigate to URL. First URL should use first RenderViewHost. 1057 const GURL url("http://www.google.com"); 1058 controller().LoadURL( 1059 url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 1060 contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); 1061 EXPECT_FALSE(contents()->cross_navigation_pending()); 1062 EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); 1063 1064 // Navigate to new site, simulating an onbeforeunload approval. 1065 const GURL url2("http://www.yahoo.com"); 1066 controller().LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 1067 EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack()); 1068 base::TimeTicks now = base::TimeTicks::Now(); 1069 orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now)); 1070 EXPECT_TRUE(contents()->cross_navigation_pending()); 1071 1072 // Simulate swap out message when the response arrives. 1073 orig_rvh->set_is_swapped_out(true); 1074 1075 // Suppose the navigation doesn't get a chance to commit, and the user 1076 // navigates in the current RVH's SiteInstance. 1077 controller().LoadURL(url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 1078 1079 // Verify that the pending navigation is cancelled and the renderer is no 1080 // longer swapped out. 1081 EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack()); 1082 SiteInstance* instance2 = contents()->GetSiteInstance(); 1083 EXPECT_FALSE(contents()->cross_navigation_pending()); 1084 EXPECT_EQ(orig_rvh, rvh()); 1085 EXPECT_FALSE(orig_rvh->is_swapped_out()); 1086 EXPECT_EQ(instance1, instance2); 1087 EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); 1088} 1089 1090// Test that NavigationEntries have the correct page state after going 1091// forward and back. Prevents regression for bug 1116137. 1092TEST_F(WebContentsImplTest, NavigationEntryContentState) { 1093 TestRenderViewHost* orig_rvh = test_rvh(); 1094 1095 // Navigate to URL. There should be no committed entry yet. 1096 const GURL url("http://www.google.com"); 1097 controller().LoadURL(url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 1098 NavigationEntry* entry = controller().GetLastCommittedEntry(); 1099 EXPECT_TRUE(entry == NULL); 1100 1101 // Committed entry should have page state after DidNavigate. 1102 contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); 1103 entry = controller().GetLastCommittedEntry(); 1104 EXPECT_TRUE(entry->GetPageState().IsValid()); 1105 1106 // Navigate to same site. 1107 const GURL url2("http://images.google.com"); 1108 controller().LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 1109 entry = controller().GetLastCommittedEntry(); 1110 EXPECT_TRUE(entry->GetPageState().IsValid()); 1111 1112 // Committed entry should have page state after DidNavigate. 1113 contents()->TestDidNavigate(orig_rvh, 2, url2, PAGE_TRANSITION_TYPED); 1114 entry = controller().GetLastCommittedEntry(); 1115 EXPECT_TRUE(entry->GetPageState().IsValid()); 1116 1117 // Now go back. Committed entry should still have page state. 1118 controller().GoBack(); 1119 contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); 1120 entry = controller().GetLastCommittedEntry(); 1121 EXPECT_TRUE(entry->GetPageState().IsValid()); 1122} 1123 1124// Test that NavigationEntries have the correct page state and SiteInstance 1125// state after opening a new window to about:blank. Prevents regression for 1126// bugs b/1116137 and http://crbug.com/111975. 1127TEST_F(WebContentsImplTest, NavigationEntryContentStateNewWindow) { 1128 TestRenderViewHost* orig_rvh = test_rvh(); 1129 1130 // When opening a new window, it is navigated to about:blank internally. 1131 // Currently, this results in two DidNavigate events. 1132 const GURL url(kAboutBlankURL); 1133 contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); 1134 contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); 1135 1136 // Should have a page state here. 1137 NavigationEntry* entry = controller().GetLastCommittedEntry(); 1138 EXPECT_TRUE(entry->GetPageState().IsValid()); 1139 1140 // The SiteInstance should be available for other navigations to use. 1141 NavigationEntryImpl* entry_impl = 1142 NavigationEntryImpl::FromNavigationEntry(entry); 1143 EXPECT_FALSE(entry_impl->site_instance()->HasSite()); 1144 int32 site_instance_id = entry_impl->site_instance()->GetId(); 1145 1146 // Navigating to a normal page should not cause a process swap. 1147 const GURL new_url("http://www.google.com"); 1148 controller().LoadURL(new_url, Referrer(), 1149 PAGE_TRANSITION_TYPED, std::string()); 1150 EXPECT_FALSE(contents()->cross_navigation_pending()); 1151 EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); 1152 contents()->TestDidNavigate(orig_rvh, 1, new_url, PAGE_TRANSITION_TYPED); 1153 NavigationEntryImpl* entry_impl2 = NavigationEntryImpl::FromNavigationEntry( 1154 controller().GetLastCommittedEntry()); 1155 EXPECT_EQ(site_instance_id, entry_impl2->site_instance()->GetId()); 1156 EXPECT_TRUE(entry_impl2->site_instance()->HasSite()); 1157} 1158 1159//////////////////////////////////////////////////////////////////////////////// 1160// Interstitial Tests 1161//////////////////////////////////////////////////////////////////////////////// 1162 1163// Test navigating to a page (with the navigation initiated from the browser, 1164// as when a URL is typed in the location bar) that shows an interstitial and 1165// creates a new navigation entry, then hiding it without proceeding. 1166TEST_F(WebContentsImplTest, 1167 ShowInterstitialFromBrowserWithNewNavigationDontProceed) { 1168 // Navigate to a page. 1169 GURL url1("http://www.google.com"); 1170 test_rvh()->SendNavigate(1, url1); 1171 EXPECT_EQ(1, controller().GetEntryCount()); 1172 1173 // Initiate a browser navigation that will trigger the interstitial 1174 controller().LoadURL(GURL("http://www.evil.com"), Referrer(), 1175 PAGE_TRANSITION_TYPED, std::string()); 1176 1177 // Show an interstitial. 1178 TestInterstitialPage::InterstitialState state = 1179 TestInterstitialPage::INVALID; 1180 bool deleted = false; 1181 GURL url2("http://interstitial"); 1182 TestInterstitialPage* interstitial = 1183 new TestInterstitialPage(contents(), true, url2, &state, &deleted); 1184 TestInterstitialPageStateGuard state_guard(interstitial); 1185 interstitial->Show(); 1186 // The interstitial should not show until its navigation has committed. 1187 EXPECT_FALSE(interstitial->is_showing()); 1188 EXPECT_FALSE(contents()->ShowingInterstitialPage()); 1189 EXPECT_TRUE(contents()->GetInterstitialPage() == NULL); 1190 // Let's commit the interstitial navigation. 1191 interstitial->TestDidNavigate(1, url2); 1192 EXPECT_TRUE(interstitial->is_showing()); 1193 EXPECT_TRUE(contents()->ShowingInterstitialPage()); 1194 EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial); 1195 NavigationEntry* entry = controller().GetActiveEntry(); 1196 ASSERT_TRUE(entry != NULL); 1197 EXPECT_TRUE(entry->GetURL() == url2); 1198 1199 // Now don't proceed. 1200 interstitial->DontProceed(); 1201 EXPECT_EQ(TestInterstitialPage::CANCELED, state); 1202 EXPECT_FALSE(contents()->ShowingInterstitialPage()); 1203 EXPECT_TRUE(contents()->GetInterstitialPage() == NULL); 1204 entry = controller().GetActiveEntry(); 1205 ASSERT_TRUE(entry != NULL); 1206 EXPECT_TRUE(entry->GetURL() == url1); 1207 EXPECT_EQ(1, controller().GetEntryCount()); 1208 1209 RunAllPendingInMessageLoop(); 1210 EXPECT_TRUE(deleted); 1211} 1212 1213// Test navigating to a page (with the navigation initiated from the renderer, 1214// as when clicking on a link in the page) that shows an interstitial and 1215// creates a new navigation entry, then hiding it without proceeding. 1216TEST_F(WebContentsImplTest, 1217 ShowInterstitiaFromRendererlWithNewNavigationDontProceed) { 1218 // Navigate to a page. 1219 GURL url1("http://www.google.com"); 1220 test_rvh()->SendNavigate(1, url1); 1221 EXPECT_EQ(1, controller().GetEntryCount()); 1222 1223 // Show an interstitial (no pending entry, the interstitial would have been 1224 // triggered by clicking on a link). 1225 TestInterstitialPage::InterstitialState state = 1226 TestInterstitialPage::INVALID; 1227 bool deleted = false; 1228 GURL url2("http://interstitial"); 1229 TestInterstitialPage* interstitial = 1230 new TestInterstitialPage(contents(), true, url2, &state, &deleted); 1231 TestInterstitialPageStateGuard state_guard(interstitial); 1232 interstitial->Show(); 1233 // The interstitial should not show until its navigation has committed. 1234 EXPECT_FALSE(interstitial->is_showing()); 1235 EXPECT_FALSE(contents()->ShowingInterstitialPage()); 1236 EXPECT_TRUE(contents()->GetInterstitialPage() == NULL); 1237 // Let's commit the interstitial navigation. 1238 interstitial->TestDidNavigate(1, url2); 1239 EXPECT_TRUE(interstitial->is_showing()); 1240 EXPECT_TRUE(contents()->ShowingInterstitialPage()); 1241 EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial); 1242 NavigationEntry* entry = controller().GetActiveEntry(); 1243 ASSERT_TRUE(entry != NULL); 1244 EXPECT_TRUE(entry->GetURL() == url2); 1245 1246 // Now don't proceed. 1247 interstitial->DontProceed(); 1248 EXPECT_EQ(TestInterstitialPage::CANCELED, state); 1249 EXPECT_FALSE(contents()->ShowingInterstitialPage()); 1250 EXPECT_TRUE(contents()->GetInterstitialPage() == NULL); 1251 entry = controller().GetActiveEntry(); 1252 ASSERT_TRUE(entry != NULL); 1253 EXPECT_TRUE(entry->GetURL() == url1); 1254 EXPECT_EQ(1, controller().GetEntryCount()); 1255 1256 RunAllPendingInMessageLoop(); 1257 EXPECT_TRUE(deleted); 1258} 1259 1260// Test navigating to a page that shows an interstitial without creating a new 1261// navigation entry (this happens when the interstitial is triggered by a 1262// sub-resource in the page), then hiding it without proceeding. 1263TEST_F(WebContentsImplTest, ShowInterstitialNoNewNavigationDontProceed) { 1264 // Navigate to a page. 1265 GURL url1("http://www.google.com"); 1266 test_rvh()->SendNavigate(1, url1); 1267 EXPECT_EQ(1, controller().GetEntryCount()); 1268 1269 // Show an interstitial. 1270 TestInterstitialPage::InterstitialState state = 1271 TestInterstitialPage::INVALID; 1272 bool deleted = false; 1273 GURL url2("http://interstitial"); 1274 TestInterstitialPage* interstitial = 1275 new TestInterstitialPage(contents(), false, url2, &state, &deleted); 1276 TestInterstitialPageStateGuard state_guard(interstitial); 1277 interstitial->Show(); 1278 // The interstitial should not show until its navigation has committed. 1279 EXPECT_FALSE(interstitial->is_showing()); 1280 EXPECT_FALSE(contents()->ShowingInterstitialPage()); 1281 EXPECT_TRUE(contents()->GetInterstitialPage() == NULL); 1282 // Let's commit the interstitial navigation. 1283 interstitial->TestDidNavigate(1, url2); 1284 EXPECT_TRUE(interstitial->is_showing()); 1285 EXPECT_TRUE(contents()->ShowingInterstitialPage()); 1286 EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial); 1287 NavigationEntry* entry = controller().GetActiveEntry(); 1288 ASSERT_TRUE(entry != NULL); 1289 // The URL specified to the interstitial should have been ignored. 1290 EXPECT_TRUE(entry->GetURL() == url1); 1291 1292 // Now don't proceed. 1293 interstitial->DontProceed(); 1294 EXPECT_EQ(TestInterstitialPage::CANCELED, state); 1295 EXPECT_FALSE(contents()->ShowingInterstitialPage()); 1296 EXPECT_TRUE(contents()->GetInterstitialPage() == NULL); 1297 entry = controller().GetActiveEntry(); 1298 ASSERT_TRUE(entry != NULL); 1299 EXPECT_TRUE(entry->GetURL() == url1); 1300 EXPECT_EQ(1, controller().GetEntryCount()); 1301 1302 RunAllPendingInMessageLoop(); 1303 EXPECT_TRUE(deleted); 1304} 1305 1306// Test navigating to a page (with the navigation initiated from the browser, 1307// as when a URL is typed in the location bar) that shows an interstitial and 1308// creates a new navigation entry, then proceeding. 1309TEST_F(WebContentsImplTest, 1310 ShowInterstitialFromBrowserNewNavigationProceed) { 1311 // Navigate to a page. 1312 GURL url1("http://www.google.com"); 1313 test_rvh()->SendNavigate(1, url1); 1314 EXPECT_EQ(1, controller().GetEntryCount()); 1315 1316 // Initiate a browser navigation that will trigger the interstitial 1317 controller().LoadURL(GURL("http://www.evil.com"), Referrer(), 1318 PAGE_TRANSITION_TYPED, std::string()); 1319 1320 // Show an interstitial. 1321 TestInterstitialPage::InterstitialState state = 1322 TestInterstitialPage::INVALID; 1323 bool deleted = false; 1324 GURL url2("http://interstitial"); 1325 TestInterstitialPage* interstitial = 1326 new TestInterstitialPage(contents(), true, url2, &state, &deleted); 1327 TestInterstitialPageStateGuard state_guard(interstitial); 1328 interstitial->Show(); 1329 // The interstitial should not show until its navigation has committed. 1330 EXPECT_FALSE(interstitial->is_showing()); 1331 EXPECT_FALSE(contents()->ShowingInterstitialPage()); 1332 EXPECT_TRUE(contents()->GetInterstitialPage() == NULL); 1333 // Let's commit the interstitial navigation. 1334 interstitial->TestDidNavigate(1, url2); 1335 EXPECT_TRUE(interstitial->is_showing()); 1336 EXPECT_TRUE(contents()->ShowingInterstitialPage()); 1337 EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial); 1338 NavigationEntry* entry = controller().GetActiveEntry(); 1339 ASSERT_TRUE(entry != NULL); 1340 EXPECT_TRUE(entry->GetURL() == url2); 1341 1342 // Then proceed. 1343 interstitial->Proceed(); 1344 // The interstitial should show until the new navigation commits. 1345 RunAllPendingInMessageLoop(); 1346 ASSERT_FALSE(deleted); 1347 EXPECT_EQ(TestInterstitialPage::OKED, state); 1348 EXPECT_TRUE(contents()->ShowingInterstitialPage()); 1349 EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial); 1350 1351 // Simulate the navigation to the page, that's when the interstitial gets 1352 // hidden. 1353 GURL url3("http://www.thepage.com"); 1354 test_rvh()->SendNavigate(2, url3); 1355 1356 EXPECT_FALSE(contents()->ShowingInterstitialPage()); 1357 EXPECT_TRUE(contents()->GetInterstitialPage() == NULL); 1358 entry = controller().GetActiveEntry(); 1359 ASSERT_TRUE(entry != NULL); 1360 EXPECT_TRUE(entry->GetURL() == url3); 1361 1362 EXPECT_EQ(2, controller().GetEntryCount()); 1363 1364 RunAllPendingInMessageLoop(); 1365 EXPECT_TRUE(deleted); 1366} 1367 1368// Test navigating to a page (with the navigation initiated from the renderer, 1369// as when clicking on a link in the page) that shows an interstitial and 1370// creates a new navigation entry, then proceeding. 1371TEST_F(WebContentsImplTest, 1372 ShowInterstitialFromRendererNewNavigationProceed) { 1373 // Navigate to a page. 1374 GURL url1("http://www.google.com"); 1375 test_rvh()->SendNavigate(1, url1); 1376 EXPECT_EQ(1, controller().GetEntryCount()); 1377 1378 // Show an interstitial. 1379 TestInterstitialPage::InterstitialState state = 1380 TestInterstitialPage::INVALID; 1381 bool deleted = false; 1382 GURL url2("http://interstitial"); 1383 TestInterstitialPage* interstitial = 1384 new TestInterstitialPage(contents(), true, url2, &state, &deleted); 1385 TestInterstitialPageStateGuard state_guard(interstitial); 1386 interstitial->Show(); 1387 // The interstitial should not show until its navigation has committed. 1388 EXPECT_FALSE(interstitial->is_showing()); 1389 EXPECT_FALSE(contents()->ShowingInterstitialPage()); 1390 EXPECT_TRUE(contents()->GetInterstitialPage() == NULL); 1391 // Let's commit the interstitial navigation. 1392 interstitial->TestDidNavigate(1, url2); 1393 EXPECT_TRUE(interstitial->is_showing()); 1394 EXPECT_TRUE(contents()->ShowingInterstitialPage()); 1395 EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial); 1396 NavigationEntry* entry = controller().GetActiveEntry(); 1397 ASSERT_TRUE(entry != NULL); 1398 EXPECT_TRUE(entry->GetURL() == url2); 1399 1400 // Then proceed. 1401 interstitial->Proceed(); 1402 // The interstitial should show until the new navigation commits. 1403 RunAllPendingInMessageLoop(); 1404 ASSERT_FALSE(deleted); 1405 EXPECT_EQ(TestInterstitialPage::OKED, state); 1406 EXPECT_TRUE(contents()->ShowingInterstitialPage()); 1407 EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial); 1408 1409 // Simulate the navigation to the page, that's when the interstitial gets 1410 // hidden. 1411 GURL url3("http://www.thepage.com"); 1412 test_rvh()->SendNavigate(2, url3); 1413 1414 EXPECT_FALSE(contents()->ShowingInterstitialPage()); 1415 EXPECT_TRUE(contents()->GetInterstitialPage() == NULL); 1416 entry = controller().GetActiveEntry(); 1417 ASSERT_TRUE(entry != NULL); 1418 EXPECT_TRUE(entry->GetURL() == url3); 1419 1420 EXPECT_EQ(2, controller().GetEntryCount()); 1421 1422 RunAllPendingInMessageLoop(); 1423 EXPECT_TRUE(deleted); 1424} 1425 1426// Test navigating to a page that shows an interstitial without creating a new 1427// navigation entry (this happens when the interstitial is triggered by a 1428// sub-resource in the page), then proceeding. 1429TEST_F(WebContentsImplTest, ShowInterstitialNoNewNavigationProceed) { 1430 // Navigate to a page so we have a navigation entry in the controller. 1431 GURL url1("http://www.google.com"); 1432 test_rvh()->SendNavigate(1, url1); 1433 EXPECT_EQ(1, controller().GetEntryCount()); 1434 1435 // Show an interstitial. 1436 TestInterstitialPage::InterstitialState state = 1437 TestInterstitialPage::INVALID; 1438 bool deleted = false; 1439 GURL url2("http://interstitial"); 1440 TestInterstitialPage* interstitial = 1441 new TestInterstitialPage(contents(), false, url2, &state, &deleted); 1442 TestInterstitialPageStateGuard state_guard(interstitial); 1443 interstitial->Show(); 1444 // The interstitial should not show until its navigation has committed. 1445 EXPECT_FALSE(interstitial->is_showing()); 1446 EXPECT_FALSE(contents()->ShowingInterstitialPage()); 1447 EXPECT_TRUE(contents()->GetInterstitialPage() == NULL); 1448 // Let's commit the interstitial navigation. 1449 interstitial->TestDidNavigate(1, url2); 1450 EXPECT_TRUE(interstitial->is_showing()); 1451 EXPECT_TRUE(contents()->ShowingInterstitialPage()); 1452 EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial); 1453 NavigationEntry* entry = controller().GetActiveEntry(); 1454 ASSERT_TRUE(entry != NULL); 1455 // The URL specified to the interstitial should have been ignored. 1456 EXPECT_TRUE(entry->GetURL() == url1); 1457 1458 // Then proceed. 1459 interstitial->Proceed(); 1460 // Since this is not a new navigation, the previous page is dismissed right 1461 // away and shows the original page. 1462 EXPECT_EQ(TestInterstitialPage::OKED, state); 1463 EXPECT_FALSE(contents()->ShowingInterstitialPage()); 1464 EXPECT_TRUE(contents()->GetInterstitialPage() == NULL); 1465 entry = controller().GetActiveEntry(); 1466 ASSERT_TRUE(entry != NULL); 1467 EXPECT_TRUE(entry->GetURL() == url1); 1468 1469 EXPECT_EQ(1, controller().GetEntryCount()); 1470 1471 RunAllPendingInMessageLoop(); 1472 EXPECT_TRUE(deleted); 1473} 1474 1475// Test navigating to a page that shows an interstitial, then navigating away. 1476TEST_F(WebContentsImplTest, ShowInterstitialThenNavigate) { 1477 // Show interstitial. 1478 TestInterstitialPage::InterstitialState state = 1479 TestInterstitialPage::INVALID; 1480 bool deleted = false; 1481 GURL url("http://interstitial"); 1482 TestInterstitialPage* interstitial = 1483 new TestInterstitialPage(contents(), true, url, &state, &deleted); 1484 TestInterstitialPageStateGuard state_guard(interstitial); 1485 interstitial->Show(); 1486 interstitial->TestDidNavigate(1, url); 1487 1488 // While interstitial showing, navigate to a new URL. 1489 const GURL url2("http://www.yahoo.com"); 1490 test_rvh()->SendNavigate(1, url2); 1491 1492 EXPECT_EQ(TestInterstitialPage::CANCELED, state); 1493 1494 RunAllPendingInMessageLoop(); 1495 EXPECT_TRUE(deleted); 1496} 1497 1498// Test navigating to a page that shows an interstitial, then going back. 1499TEST_F(WebContentsImplTest, ShowInterstitialThenGoBack) { 1500 // Navigate to a page so we have a navigation entry in the controller. 1501 GURL url1("http://www.google.com"); 1502 test_rvh()->SendNavigate(1, url1); 1503 EXPECT_EQ(1, controller().GetEntryCount()); 1504 1505 // Show interstitial. 1506 TestInterstitialPage::InterstitialState state = 1507 TestInterstitialPage::INVALID; 1508 bool deleted = false; 1509 GURL interstitial_url("http://interstitial"); 1510 TestInterstitialPage* interstitial = 1511 new TestInterstitialPage(contents(), true, interstitial_url, 1512 &state, &deleted); 1513 TestInterstitialPageStateGuard state_guard(interstitial); 1514 interstitial->Show(); 1515 interstitial->TestDidNavigate(2, interstitial_url); 1516 1517 // While the interstitial is showing, go back. 1518 controller().GoBack(); 1519 test_rvh()->SendNavigate(1, url1); 1520 1521 // Make sure we are back to the original page and that the interstitial is 1522 // gone. 1523 EXPECT_EQ(TestInterstitialPage::CANCELED, state); 1524 NavigationEntry* entry = controller().GetActiveEntry(); 1525 ASSERT_TRUE(entry); 1526 EXPECT_EQ(url1.spec(), entry->GetURL().spec()); 1527 1528 RunAllPendingInMessageLoop(); 1529 EXPECT_TRUE(deleted); 1530} 1531 1532// Test navigating to a page that shows an interstitial, has a renderer crash, 1533// and then goes back. 1534TEST_F(WebContentsImplTest, ShowInterstitialCrashRendererThenGoBack) { 1535 // Navigate to a page so we have a navigation entry in the controller. 1536 GURL url1("http://www.google.com"); 1537 test_rvh()->SendNavigate(1, url1); 1538 EXPECT_EQ(1, controller().GetEntryCount()); 1539 1540 // Show interstitial. 1541 TestInterstitialPage::InterstitialState state = 1542 TestInterstitialPage::INVALID; 1543 bool deleted = false; 1544 GURL interstitial_url("http://interstitial"); 1545 TestInterstitialPage* interstitial = 1546 new TestInterstitialPage(contents(), true, interstitial_url, 1547 &state, &deleted); 1548 TestInterstitialPageStateGuard state_guard(interstitial); 1549 interstitial->Show(); 1550 interstitial->TestDidNavigate(2, interstitial_url); 1551 1552 // Crash the renderer 1553 test_rvh()->OnMessageReceived( 1554 ViewHostMsg_RenderViewGone( 1555 0, base::TERMINATION_STATUS_PROCESS_CRASHED, -1)); 1556 1557 // While the interstitial is showing, go back. 1558 controller().GoBack(); 1559 test_rvh()->SendNavigate(1, url1); 1560 1561 // Make sure we are back to the original page and that the interstitial is 1562 // gone. 1563 EXPECT_EQ(TestInterstitialPage::CANCELED, state); 1564 NavigationEntry* entry = controller().GetActiveEntry(); 1565 ASSERT_TRUE(entry); 1566 EXPECT_EQ(url1.spec(), entry->GetURL().spec()); 1567 1568 RunAllPendingInMessageLoop(); 1569 EXPECT_TRUE(deleted); 1570} 1571 1572// Test navigating to a page that shows an interstitial, has the renderer crash, 1573// and then navigates to the interstitial. 1574TEST_F(WebContentsImplTest, ShowInterstitialCrashRendererThenNavigate) { 1575 // Navigate to a page so we have a navigation entry in the controller. 1576 GURL url1("http://www.google.com"); 1577 test_rvh()->SendNavigate(1, url1); 1578 EXPECT_EQ(1, controller().GetEntryCount()); 1579 1580 // Show interstitial. 1581 TestInterstitialPage::InterstitialState state = 1582 TestInterstitialPage::INVALID; 1583 bool deleted = false; 1584 GURL interstitial_url("http://interstitial"); 1585 TestInterstitialPage* interstitial = 1586 new TestInterstitialPage(contents(), true, interstitial_url, 1587 &state, &deleted); 1588 TestInterstitialPageStateGuard state_guard(interstitial); 1589 interstitial->Show(); 1590 1591 // Crash the renderer 1592 test_rvh()->OnMessageReceived( 1593 ViewHostMsg_RenderViewGone( 1594 0, base::TERMINATION_STATUS_PROCESS_CRASHED, -1)); 1595 1596 interstitial->TestDidNavigate(2, interstitial_url); 1597} 1598 1599// Test navigating to a page that shows an interstitial, then close the 1600// contents. 1601TEST_F(WebContentsImplTest, ShowInterstitialThenCloseTab) { 1602 // Show interstitial. 1603 TestInterstitialPage::InterstitialState state = 1604 TestInterstitialPage::INVALID; 1605 bool deleted = false; 1606 GURL url("http://interstitial"); 1607 TestInterstitialPage* interstitial = 1608 new TestInterstitialPage(contents(), true, url, &state, &deleted); 1609 TestInterstitialPageStateGuard state_guard(interstitial); 1610 interstitial->Show(); 1611 interstitial->TestDidNavigate(1, url); 1612 1613 // Now close the contents. 1614 DeleteContents(); 1615 EXPECT_EQ(TestInterstitialPage::CANCELED, state); 1616 1617 RunAllPendingInMessageLoop(); 1618 EXPECT_TRUE(deleted); 1619} 1620 1621// Test navigating to a page that shows an interstitial, then close the 1622// contents. 1623TEST_F(WebContentsImplTest, ShowInterstitialThenCloseAndShutdown) { 1624 // Show interstitial. 1625 TestInterstitialPage::InterstitialState state = 1626 TestInterstitialPage::INVALID; 1627 bool deleted = false; 1628 GURL url("http://interstitial"); 1629 TestInterstitialPage* interstitial = 1630 new TestInterstitialPage(contents(), true, url, &state, &deleted); 1631 TestInterstitialPageStateGuard state_guard(interstitial); 1632 interstitial->Show(); 1633 interstitial->TestDidNavigate(1, url); 1634 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( 1635 interstitial->GetRenderViewHostForTesting()); 1636 1637 // Now close the contents. 1638 DeleteContents(); 1639 EXPECT_EQ(TestInterstitialPage::CANCELED, state); 1640 1641 // Before the interstitial has a chance to process its shutdown task, 1642 // simulate quitting the browser. This goes through all processes and 1643 // tells them to destruct. 1644 rvh->OnMessageReceived( 1645 ViewHostMsg_RenderViewGone(0, 0, 0)); 1646 1647 RunAllPendingInMessageLoop(); 1648 EXPECT_TRUE(deleted); 1649} 1650 1651// Test that after Proceed is called and an interstitial is still shown, no more 1652// commands get executed. 1653TEST_F(WebContentsImplTest, ShowInterstitialProceedMultipleCommands) { 1654 // Navigate to a page so we have a navigation entry in the controller. 1655 GURL url1("http://www.google.com"); 1656 test_rvh()->SendNavigate(1, url1); 1657 EXPECT_EQ(1, controller().GetEntryCount()); 1658 1659 // Show an interstitial. 1660 TestInterstitialPage::InterstitialState state = 1661 TestInterstitialPage::INVALID; 1662 bool deleted = false; 1663 GURL url2("http://interstitial"); 1664 TestInterstitialPage* interstitial = 1665 new TestInterstitialPage(contents(), true, url2, &state, &deleted); 1666 TestInterstitialPageStateGuard state_guard(interstitial); 1667 interstitial->Show(); 1668 interstitial->TestDidNavigate(1, url2); 1669 1670 // Run a command. 1671 EXPECT_EQ(0, interstitial->command_received_count()); 1672 interstitial->TestDomOperationResponse("toto"); 1673 EXPECT_EQ(1, interstitial->command_received_count()); 1674 1675 // Then proceed. 1676 interstitial->Proceed(); 1677 RunAllPendingInMessageLoop(); 1678 ASSERT_FALSE(deleted); 1679 1680 // While the navigation to the new page is pending, send other commands, they 1681 // should be ignored. 1682 interstitial->TestDomOperationResponse("hello"); 1683 interstitial->TestDomOperationResponse("hi"); 1684 EXPECT_EQ(1, interstitial->command_received_count()); 1685} 1686 1687// Test showing an interstitial while another interstitial is already showing. 1688TEST_F(WebContentsImplTest, ShowInterstitialOnInterstitial) { 1689 // Navigate to a page so we have a navigation entry in the controller. 1690 GURL start_url("http://www.google.com"); 1691 test_rvh()->SendNavigate(1, start_url); 1692 EXPECT_EQ(1, controller().GetEntryCount()); 1693 1694 // Show an interstitial. 1695 TestInterstitialPage::InterstitialState state1 = 1696 TestInterstitialPage::INVALID; 1697 bool deleted1 = false; 1698 GURL url1("http://interstitial1"); 1699 TestInterstitialPage* interstitial1 = 1700 new TestInterstitialPage(contents(), true, url1, &state1, &deleted1); 1701 TestInterstitialPageStateGuard state_guard1(interstitial1); 1702 interstitial1->Show(); 1703 interstitial1->TestDidNavigate(1, url1); 1704 1705 // Now show another interstitial. 1706 TestInterstitialPage::InterstitialState state2 = 1707 TestInterstitialPage::INVALID; 1708 bool deleted2 = false; 1709 GURL url2("http://interstitial2"); 1710 TestInterstitialPage* interstitial2 = 1711 new TestInterstitialPage(contents(), true, url2, &state2, &deleted2); 1712 TestInterstitialPageStateGuard state_guard2(interstitial2); 1713 interstitial2->Show(); 1714 interstitial2->TestDidNavigate(1, url2); 1715 1716 // Showing interstitial2 should have caused interstitial1 to go away. 1717 EXPECT_EQ(TestInterstitialPage::CANCELED, state1); 1718 EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2); 1719 1720 RunAllPendingInMessageLoop(); 1721 EXPECT_TRUE(deleted1); 1722 ASSERT_FALSE(deleted2); 1723 1724 // Let's make sure interstitial2 is working as intended. 1725 interstitial2->Proceed(); 1726 GURL landing_url("http://www.thepage.com"); 1727 test_rvh()->SendNavigate(2, landing_url); 1728 1729 EXPECT_FALSE(contents()->ShowingInterstitialPage()); 1730 EXPECT_TRUE(contents()->GetInterstitialPage() == NULL); 1731 NavigationEntry* entry = controller().GetActiveEntry(); 1732 ASSERT_TRUE(entry != NULL); 1733 EXPECT_TRUE(entry->GetURL() == landing_url); 1734 EXPECT_EQ(2, controller().GetEntryCount()); 1735 RunAllPendingInMessageLoop(); 1736 EXPECT_TRUE(deleted2); 1737} 1738 1739// Test showing an interstitial, proceeding and then navigating to another 1740// interstitial. 1741TEST_F(WebContentsImplTest, ShowInterstitialProceedShowInterstitial) { 1742 // Navigate to a page so we have a navigation entry in the controller. 1743 GURL start_url("http://www.google.com"); 1744 test_rvh()->SendNavigate(1, start_url); 1745 EXPECT_EQ(1, controller().GetEntryCount()); 1746 1747 // Show an interstitial. 1748 TestInterstitialPage::InterstitialState state1 = 1749 TestInterstitialPage::INVALID; 1750 bool deleted1 = false; 1751 GURL url1("http://interstitial1"); 1752 TestInterstitialPage* interstitial1 = 1753 new TestInterstitialPage(contents(), true, url1, &state1, &deleted1); 1754 TestInterstitialPageStateGuard state_guard1(interstitial1); 1755 interstitial1->Show(); 1756 interstitial1->TestDidNavigate(1, url1); 1757 1758 // Take action. The interstitial won't be hidden until the navigation is 1759 // committed. 1760 interstitial1->Proceed(); 1761 EXPECT_EQ(TestInterstitialPage::OKED, state1); 1762 1763 // Now show another interstitial (simulating the navigation causing another 1764 // interstitial). 1765 TestInterstitialPage::InterstitialState state2 = 1766 TestInterstitialPage::INVALID; 1767 bool deleted2 = false; 1768 GURL url2("http://interstitial2"); 1769 TestInterstitialPage* interstitial2 = 1770 new TestInterstitialPage(contents(), true, url2, &state2, &deleted2); 1771 TestInterstitialPageStateGuard state_guard2(interstitial2); 1772 interstitial2->Show(); 1773 interstitial2->TestDidNavigate(1, url2); 1774 1775 // Showing interstitial2 should have caused interstitial1 to go away. 1776 EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2); 1777 RunAllPendingInMessageLoop(); 1778 EXPECT_TRUE(deleted1); 1779 ASSERT_FALSE(deleted2); 1780 1781 // Let's make sure interstitial2 is working as intended. 1782 interstitial2->Proceed(); 1783 GURL landing_url("http://www.thepage.com"); 1784 test_rvh()->SendNavigate(2, landing_url); 1785 1786 RunAllPendingInMessageLoop(); 1787 EXPECT_TRUE(deleted2); 1788 EXPECT_FALSE(contents()->ShowingInterstitialPage()); 1789 EXPECT_TRUE(contents()->GetInterstitialPage() == NULL); 1790 NavigationEntry* entry = controller().GetActiveEntry(); 1791 ASSERT_TRUE(entry != NULL); 1792 EXPECT_TRUE(entry->GetURL() == landing_url); 1793 EXPECT_EQ(2, controller().GetEntryCount()); 1794} 1795 1796// Test that navigating away from an interstitial while it's loading cause it 1797// not to show. 1798TEST_F(WebContentsImplTest, NavigateBeforeInterstitialShows) { 1799 // Show an interstitial. 1800 TestInterstitialPage::InterstitialState state = 1801 TestInterstitialPage::INVALID; 1802 bool deleted = false; 1803 GURL interstitial_url("http://interstitial"); 1804 TestInterstitialPage* interstitial = 1805 new TestInterstitialPage(contents(), true, interstitial_url, 1806 &state, &deleted); 1807 TestInterstitialPageStateGuard state_guard(interstitial); 1808 interstitial->Show(); 1809 1810 // Let's simulate a navigation initiated from the browser before the 1811 // interstitial finishes loading. 1812 const GURL url("http://www.google.com"); 1813 controller().LoadURL(url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 1814 EXPECT_FALSE(interstitial->is_showing()); 1815 RunAllPendingInMessageLoop(); 1816 ASSERT_FALSE(deleted); 1817 1818 // Now let's make the interstitial navigation commit. 1819 interstitial->TestDidNavigate(1, interstitial_url); 1820 1821 // After it loaded the interstitial should be gone. 1822 EXPECT_EQ(TestInterstitialPage::CANCELED, state); 1823 1824 RunAllPendingInMessageLoop(); 1825 EXPECT_TRUE(deleted); 1826} 1827 1828// Test that a new request to show an interstitial while an interstitial is 1829// pending does not cause problems. htp://crbug/29655 and htp://crbug/9442. 1830TEST_F(WebContentsImplTest, TwoQuickInterstitials) { 1831 GURL interstitial_url("http://interstitial"); 1832 1833 // Show a first interstitial. 1834 TestInterstitialPage::InterstitialState state1 = 1835 TestInterstitialPage::INVALID; 1836 bool deleted1 = false; 1837 TestInterstitialPage* interstitial1 = 1838 new TestInterstitialPage(contents(), true, interstitial_url, 1839 &state1, &deleted1); 1840 TestInterstitialPageStateGuard state_guard1(interstitial1); 1841 interstitial1->Show(); 1842 1843 // Show another interstitial on that same contents before the first one had 1844 // time to load. 1845 TestInterstitialPage::InterstitialState state2 = 1846 TestInterstitialPage::INVALID; 1847 bool deleted2 = false; 1848 TestInterstitialPage* interstitial2 = 1849 new TestInterstitialPage(contents(), true, interstitial_url, 1850 &state2, &deleted2); 1851 TestInterstitialPageStateGuard state_guard2(interstitial2); 1852 interstitial2->Show(); 1853 1854 // The first interstitial should have been closed and deleted. 1855 EXPECT_EQ(TestInterstitialPage::CANCELED, state1); 1856 // The 2nd one should still be OK. 1857 EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2); 1858 1859 RunAllPendingInMessageLoop(); 1860 EXPECT_TRUE(deleted1); 1861 ASSERT_FALSE(deleted2); 1862 1863 // Make the interstitial navigation commit it should be showing. 1864 interstitial2->TestDidNavigate(1, interstitial_url); 1865 EXPECT_EQ(interstitial2, contents()->GetInterstitialPage()); 1866} 1867 1868// Test showing an interstitial and have its renderer crash. 1869TEST_F(WebContentsImplTest, InterstitialCrasher) { 1870 // Show an interstitial. 1871 TestInterstitialPage::InterstitialState state = 1872 TestInterstitialPage::INVALID; 1873 bool deleted = false; 1874 GURL url("http://interstitial"); 1875 TestInterstitialPage* interstitial = 1876 new TestInterstitialPage(contents(), true, url, &state, &deleted); 1877 TestInterstitialPageStateGuard state_guard(interstitial); 1878 interstitial->Show(); 1879 // Simulate a renderer crash before the interstitial is shown. 1880 interstitial->TestRenderViewTerminated( 1881 base::TERMINATION_STATUS_PROCESS_CRASHED, -1); 1882 // The interstitial should have been dismissed. 1883 EXPECT_EQ(TestInterstitialPage::CANCELED, state); 1884 RunAllPendingInMessageLoop(); 1885 EXPECT_TRUE(deleted); 1886 1887 // Now try again but this time crash the intersitial after it was shown. 1888 interstitial = 1889 new TestInterstitialPage(contents(), true, url, &state, &deleted); 1890 interstitial->Show(); 1891 interstitial->TestDidNavigate(1, url); 1892 // Simulate a renderer crash. 1893 interstitial->TestRenderViewTerminated( 1894 base::TERMINATION_STATUS_PROCESS_CRASHED, -1); 1895 // The interstitial should have been dismissed. 1896 EXPECT_EQ(TestInterstitialPage::CANCELED, state); 1897 RunAllPendingInMessageLoop(); 1898 EXPECT_TRUE(deleted); 1899} 1900 1901// Tests that showing an interstitial as a result of a browser initiated 1902// navigation while an interstitial is showing does not remove the pending 1903// entry (see http://crbug.com/9791). 1904TEST_F(WebContentsImplTest, NewInterstitialDoesNotCancelPendingEntry) { 1905 const char kUrl[] = "http://www.badguys.com/"; 1906 const GURL kGURL(kUrl); 1907 1908 // Start a navigation to a page 1909 contents()->GetController().LoadURL( 1910 kGURL, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 1911 1912 // Simulate that navigation triggering an interstitial. 1913 TestInterstitialPage::InterstitialState state = 1914 TestInterstitialPage::INVALID; 1915 bool deleted = false; 1916 TestInterstitialPage* interstitial = 1917 new TestInterstitialPage(contents(), true, kGURL, &state, &deleted); 1918 TestInterstitialPageStateGuard state_guard(interstitial); 1919 interstitial->Show(); 1920 interstitial->TestDidNavigate(1, kGURL); 1921 1922 // Initiate a new navigation from the browser that also triggers an 1923 // interstitial. 1924 contents()->GetController().LoadURL( 1925 kGURL, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 1926 TestInterstitialPage::InterstitialState state2 = 1927 TestInterstitialPage::INVALID; 1928 bool deleted2 = false; 1929 TestInterstitialPage* interstitial2 = 1930 new TestInterstitialPage(contents(), true, kGURL, &state2, &deleted2); 1931 TestInterstitialPageStateGuard state_guard2(interstitial2); 1932 interstitial2->Show(); 1933 interstitial2->TestDidNavigate(1, kGURL); 1934 1935 // Make sure we still have an entry. 1936 NavigationEntry* entry = contents()->GetController().GetPendingEntry(); 1937 ASSERT_TRUE(entry); 1938 EXPECT_EQ(kUrl, entry->GetURL().spec()); 1939 1940 // And that the first interstitial is gone, but not the second. 1941 EXPECT_EQ(TestInterstitialPage::CANCELED, state); 1942 EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2); 1943 RunAllPendingInMessageLoop(); 1944 EXPECT_TRUE(deleted); 1945 EXPECT_FALSE(deleted2); 1946} 1947 1948// Tests that Javascript messages are not shown while an interstitial is 1949// showing. 1950TEST_F(WebContentsImplTest, NoJSMessageOnInterstitials) { 1951 const char kUrl[] = "http://www.badguys.com/"; 1952 const GURL kGURL(kUrl); 1953 1954 // Start a navigation to a page 1955 contents()->GetController().LoadURL( 1956 kGURL, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 1957 // DidNavigate from the page 1958 contents()->TestDidNavigate(rvh(), 1, kGURL, PAGE_TRANSITION_TYPED); 1959 1960 // Simulate showing an interstitial while the page is showing. 1961 TestInterstitialPage::InterstitialState state = 1962 TestInterstitialPage::INVALID; 1963 bool deleted = false; 1964 TestInterstitialPage* interstitial = 1965 new TestInterstitialPage(contents(), true, kGURL, &state, &deleted); 1966 TestInterstitialPageStateGuard state_guard(interstitial); 1967 interstitial->Show(); 1968 interstitial->TestDidNavigate(1, kGURL); 1969 1970 // While the interstitial is showing, let's simulate the hidden page 1971 // attempting to show a JS message. 1972 IPC::Message* dummy_message = new IPC::Message; 1973 bool did_suppress_message = false; 1974 contents()->RunJavaScriptMessage(contents()->GetRenderViewHost(), 1975 ASCIIToUTF16("This is an informative message"), ASCIIToUTF16("OK"), 1976 kGURL, JAVASCRIPT_MESSAGE_TYPE_ALERT, dummy_message, 1977 &did_suppress_message); 1978 EXPECT_TRUE(did_suppress_message); 1979} 1980 1981// Makes sure that if the source passed to CopyStateFromAndPrune has an 1982// interstitial it isn't copied over to the destination. 1983TEST_F(WebContentsImplTest, CopyStateFromAndPruneSourceInterstitial) { 1984 // Navigate to a page. 1985 GURL url1("http://www.google.com"); 1986 test_rvh()->SendNavigate(1, url1); 1987 EXPECT_EQ(1, controller().GetEntryCount()); 1988 1989 // Initiate a browser navigation that will trigger the interstitial 1990 controller().LoadURL(GURL("http://www.evil.com"), Referrer(), 1991 PAGE_TRANSITION_TYPED, std::string()); 1992 1993 // Show an interstitial. 1994 TestInterstitialPage::InterstitialState state = 1995 TestInterstitialPage::INVALID; 1996 bool deleted = false; 1997 GURL url2("http://interstitial"); 1998 TestInterstitialPage* interstitial = 1999 new TestInterstitialPage(contents(), true, url2, &state, &deleted); 2000 TestInterstitialPageStateGuard state_guard(interstitial); 2001 interstitial->Show(); 2002 interstitial->TestDidNavigate(1, url2); 2003 EXPECT_TRUE(interstitial->is_showing()); 2004 EXPECT_EQ(2, controller().GetEntryCount()); 2005 2006 // Create another NavigationController. 2007 GURL url3("http://foo2"); 2008 scoped_ptr<TestWebContents> other_contents( 2009 static_cast<TestWebContents*>(CreateTestWebContents())); 2010 NavigationControllerImpl& other_controller = other_contents->GetController(); 2011 other_contents->NavigateAndCommit(url3); 2012 other_contents->ExpectSetHistoryLengthAndPrune( 2013 NavigationEntryImpl::FromNavigationEntry( 2014 other_controller.GetEntryAtIndex(0))->site_instance(), 1, 2015 other_controller.GetEntryAtIndex(0)->GetPageID()); 2016 other_controller.CopyStateFromAndPrune(&controller()); 2017 2018 // The merged controller should only have two entries: url1 and url2. 2019 ASSERT_EQ(2, other_controller.GetEntryCount()); 2020 EXPECT_EQ(1, other_controller.GetCurrentEntryIndex()); 2021 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL()); 2022 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL()); 2023 2024 // And the merged controller shouldn't be showing an interstitial. 2025 EXPECT_FALSE(other_contents->ShowingInterstitialPage()); 2026} 2027 2028// Makes sure that CopyStateFromAndPrune cannot be called if the target is 2029// showing an interstitial. 2030TEST_F(WebContentsImplTest, CopyStateFromAndPruneTargetInterstitial) { 2031 // Navigate to a page. 2032 GURL url1("http://www.google.com"); 2033 contents()->NavigateAndCommit(url1); 2034 2035 // Create another NavigationController. 2036 scoped_ptr<TestWebContents> other_contents( 2037 static_cast<TestWebContents*>(CreateTestWebContents())); 2038 NavigationControllerImpl& other_controller = other_contents->GetController(); 2039 2040 // Navigate it to url2. 2041 GURL url2("http://foo2"); 2042 other_contents->NavigateAndCommit(url2); 2043 2044 // Show an interstitial. 2045 TestInterstitialPage::InterstitialState state = 2046 TestInterstitialPage::INVALID; 2047 bool deleted = false; 2048 GURL url3("http://interstitial"); 2049 TestInterstitialPage* interstitial = 2050 new TestInterstitialPage(other_contents.get(), true, url3, &state, 2051 &deleted); 2052 TestInterstitialPageStateGuard state_guard(interstitial); 2053 interstitial->Show(); 2054 interstitial->TestDidNavigate(1, url3); 2055 EXPECT_TRUE(interstitial->is_showing()); 2056 EXPECT_EQ(2, other_controller.GetEntryCount()); 2057 2058 // Ensure that we do not allow calling CopyStateFromAndPrune when an 2059 // interstitial is showing in the target. 2060 EXPECT_FALSE(other_controller.CanPruneAllButVisible()); 2061} 2062 2063// Regression test for http://crbug.com/168611 - the URLs passed by the 2064// DidFinishLoad and DidFailLoadWithError IPCs should get filtered. 2065TEST_F(WebContentsImplTest, FilterURLs) { 2066 TestWebContentsObserver observer(contents()); 2067 2068 // A navigation to about:whatever should always look like a navigation to 2069 // about:blank 2070 GURL url_normalized("about:blank"); 2071 GURL url_from_ipc("about:whatever"); 2072 2073 // We navigate the test WebContents to about:blank, since NavigateAndCommit 2074 // will use the given URL to create the NavigationEntry as well, and that 2075 // entry should contain the filtered URL. 2076 contents()->NavigateAndCommit(url_normalized); 2077 2078 // Check that an IPC with about:whatever is correctly normalized. 2079 contents()->TestDidFinishLoad(1, url_from_ipc, true); 2080 2081 EXPECT_EQ(url_normalized, observer.last_url()); 2082 2083 // Create and navigate another WebContents. 2084 scoped_ptr<TestWebContents> other_contents( 2085 static_cast<TestWebContents*>(CreateTestWebContents())); 2086 TestWebContentsObserver other_observer(other_contents.get()); 2087 other_contents->NavigateAndCommit(url_normalized); 2088 2089 // Check that an IPC with about:whatever is correctly normalized. 2090 other_contents->TestDidFailLoadWithError( 2091 1, url_from_ipc, true, 1, string16()); 2092 EXPECT_EQ(url_normalized, other_observer.last_url()); 2093} 2094 2095// Test that if a pending contents is deleted before it is shown, we don't 2096// crash. 2097TEST_F(WebContentsImplTest, PendingContents) { 2098 scoped_ptr<TestWebContents> other_contents( 2099 static_cast<TestWebContents*>(CreateTestWebContents())); 2100 contents()->AddPendingContents(other_contents.get()); 2101 int route_id = other_contents->GetRenderViewHost()->GetRoutingID(); 2102 other_contents.reset(); 2103 EXPECT_EQ(NULL, contents()->GetCreatedWindow(route_id)); 2104} 2105 2106// This test asserts the shape of the frame tree is correct, based on incoming 2107// frame attached/detached messages. 2108TEST_F(WebContentsImplTest, FrameTreeShape) { 2109 std::string no_children_node("no children node"); 2110 std::string deep_subtree("node with deep subtree"); 2111 2112 // The initial navigation will create a frame_tree_root_ node with the top 2113 // level frame id. Simulate that by just creating it here. 2114 contents()->frame_tree_root_.reset( 2115 new FrameTreeNode(5, std::string("top-level"))); 2116 2117 // Let's send a series of messages for frame attached and build the 2118 // frame tree. 2119 contents()->OnFrameAttached(5, 14, std::string()); 2120 contents()->OnFrameAttached(5, 15, std::string()); 2121 contents()->OnFrameAttached(5, 16, std::string()); 2122 2123 contents()->OnFrameAttached(14, 244, std::string()); 2124 contents()->OnFrameAttached(14, 245, std::string()); 2125 2126 contents()->OnFrameAttached(15, 255, no_children_node); 2127 2128 contents()->OnFrameAttached(16, 264, std::string()); 2129 contents()->OnFrameAttached(16, 265, std::string()); 2130 contents()->OnFrameAttached(16, 266, std::string()); 2131 contents()->OnFrameAttached(16, 267, deep_subtree); 2132 contents()->OnFrameAttached(16, 268, std::string()); 2133 2134 contents()->OnFrameAttached(267, 365, std::string()); 2135 contents()->OnFrameAttached(365, 455, std::string()); 2136 contents()->OnFrameAttached(455, 555, std::string()); 2137 contents()->OnFrameAttached(555, 655, std::string()); 2138 2139 // Now, verify the tree structure is as expected. 2140 FrameTreeNode* root = contents()->frame_tree_root_.get(); 2141 EXPECT_EQ(5, root->frame_id()); 2142 EXPECT_EQ(3UL, root->child_count()); 2143 2144 EXPECT_EQ(2UL, root->child_at(0)->child_count()); 2145 EXPECT_EQ(0UL, root->child_at(0)->child_at(0)->child_count()); 2146 EXPECT_EQ(0UL, root->child_at(0)->child_at(1)->child_count()); 2147 2148 EXPECT_EQ(1UL, root->child_at(1)->child_count()); 2149 EXPECT_EQ(0UL, root->child_at(1)->child_at(0)->child_count()); 2150 EXPECT_STREQ(no_children_node.c_str(), 2151 root->child_at(1)->child_at(0)->frame_name().c_str()); 2152 2153 EXPECT_EQ(5UL, root->child_at(2)->child_count()); 2154 EXPECT_EQ(0UL, root->child_at(2)->child_at(0)->child_count()); 2155 EXPECT_EQ(0UL, root->child_at(2)->child_at(1)->child_count()); 2156 EXPECT_EQ(0UL, root->child_at(2)->child_at(2)->child_count()); 2157 EXPECT_EQ(1UL, root->child_at(2)->child_at(3)->child_count()); 2158 EXPECT_STREQ(deep_subtree.c_str(), 2159 root->child_at(2)->child_at(3)->frame_name().c_str()); 2160 EXPECT_EQ(0UL, root->child_at(2)->child_at(4)->child_count()); 2161 2162 FrameTreeNode* deep_tree = root->child_at(2)->child_at(3)->child_at(0); 2163 EXPECT_EQ(365, deep_tree->frame_id()); 2164 EXPECT_EQ(1UL, deep_tree->child_count()); 2165 EXPECT_EQ(455, deep_tree->child_at(0)->frame_id()); 2166 EXPECT_EQ(1UL, deep_tree->child_at(0)->child_count()); 2167 EXPECT_EQ(555, deep_tree->child_at(0)->child_at(0)->frame_id()); 2168 EXPECT_EQ(1UL, deep_tree->child_at(0)->child_at(0)->child_count()); 2169 EXPECT_EQ(655, deep_tree->child_at(0)->child_at(0)->child_at(0)->frame_id()); 2170 EXPECT_EQ(0UL, 2171 deep_tree->child_at(0)->child_at(0)->child_at(0)->child_count()); 2172 2173 // Test removing of nodes. 2174 contents()->OnFrameDetached(555, 655); 2175 EXPECT_EQ(0UL, deep_tree->child_at(0)->child_at(0)->child_count()); 2176 2177 contents()->OnFrameDetached(16, 265); 2178 EXPECT_EQ(4UL, root->child_at(2)->child_count()); 2179 2180 contents()->OnFrameDetached(5, 15); 2181 EXPECT_EQ(2UL, root->child_count()); 2182} 2183 2184} // namespace content 2185