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