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