web_contents_impl_unittest.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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/interstitial_page_delegate.h" 16#include "content/public/browser/navigation_details.h" 17#include "content/public/browser/notification_details.h" 18#include "content/public/browser/notification_source.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 34namespace content { 35namespace { 36 37const char kTestWebUIUrl[] = "chrome://blah"; 38 39class 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 47 return new WebUIController(web_ui); 48 } 49 50 virtual WebUI::TypeID GetWebUIType(BrowserContext* browser_context, 51 const GURL& url) const OVERRIDE { 52 return WebUI::kNoWebUI; 53 } 54 55 virtual bool UseWebUIForURL(BrowserContext* browser_context, 56 const GURL& url) const OVERRIDE { 57 return UseWebUI(url); 58 } 59 60 virtual bool UseWebUIBindingsForURL(BrowserContext* browser_context, 61 const GURL& url) const OVERRIDE { 62 return UseWebUI(url); 63 } 64 65 private: 66 bool UseWebUI(const GURL& url) const { 67 return url == GURL(kTestWebUIUrl); 68 } 69}; 70 71class TestInterstitialPage; 72 73class TestInterstitialPageDelegate : public InterstitialPageDelegate { 74 public: 75 TestInterstitialPageDelegate(TestInterstitialPage* interstitial_page) 76 : interstitial_page_(interstitial_page) {} 77 virtual void CommandReceived(const std::string& command) OVERRIDE; 78 virtual std::string GetHTMLContents() OVERRIDE { return std::string(); } 79 virtual void OnDontProceed() OVERRIDE; 80 virtual void OnProceed() OVERRIDE; 81 private: 82 TestInterstitialPage* interstitial_page_; 83}; 84 85class TestInterstitialPage : public InterstitialPageImpl { 86 public: 87 enum InterstitialState { 88 INVALID = 0, // Hasn't yet been initialized. 89 UNDECIDED, // Initialized, but no decision taken yet. 90 OKED, // Proceed was called. 91 CANCELED // DontProceed was called. 92 }; 93 94 class Delegate { 95 public: 96 virtual void TestInterstitialPageDeleted( 97 TestInterstitialPage* interstitial) = 0; 98 99 protected: 100 virtual ~Delegate() {} 101 }; 102 103 // IMPORTANT NOTE: if you pass stack allocated values for |state| and 104 // |deleted| (like all interstitial related tests do at this point), make sure 105 // to create an instance of the TestInterstitialPageStateGuard class on the 106 // stack in your test. This will ensure that the TestInterstitialPage states 107 // are cleared when the test finishes. 108 // Not doing so will cause stack trashing if your test does not hide the 109 // interstitial, as in such a case it will be destroyed in the test TearDown 110 // method and will dereference the |deleted| local variable which by then is 111 // out of scope. 112 TestInterstitialPage(WebContentsImpl* contents, 113 bool new_navigation, 114 const GURL& url, 115 InterstitialState* state, 116 bool* deleted) 117 : InterstitialPageImpl( 118 contents, new_navigation, url, 119 new TestInterstitialPageDelegate(this)), 120 state_(state), 121 deleted_(deleted), 122 command_received_count_(0), 123 delegate_(NULL) { 124 *state_ = UNDECIDED; 125 *deleted_ = false; 126 } 127 128 virtual ~TestInterstitialPage() { 129 if (deleted_) 130 *deleted_ = true; 131 if (delegate_) 132 delegate_->TestInterstitialPageDeleted(this); 133 } 134 135 void OnDontProceed() { 136 if (state_) 137 *state_ = CANCELED; 138 } 139 void OnProceed() { 140 if (state_) 141 *state_ = OKED; 142 } 143 144 int command_received_count() const { 145 return command_received_count_; 146 } 147 148 void TestDomOperationResponse(const std::string& json_string) { 149 if (enabled()) 150 CommandReceived(); 151 } 152 153 void TestDidNavigate(int page_id, const GURL& url) { 154 ViewHostMsg_FrameNavigate_Params params; 155 InitNavigateParams(¶ms, page_id, url, PAGE_TRANSITION_TYPED); 156 DidNavigate(GetRenderViewHostForTesting(), params); 157 } 158 159 void TestRenderViewTerminated(base::TerminationStatus status, 160 int error_code) { 161 RenderViewTerminated(GetRenderViewHostForTesting(), status, error_code); 162 } 163 164 bool is_showing() const { 165 return static_cast<TestRenderWidgetHostView*>( 166 GetRenderViewHostForTesting()->GetView())->is_showing(); 167 } 168 169 void ClearStates() { 170 state_ = NULL; 171 deleted_ = NULL; 172 delegate_ = NULL; 173 } 174 175 void CommandReceived() { 176 command_received_count_++; 177 } 178 179 void set_delegate(Delegate* delegate) { 180 delegate_ = delegate; 181 } 182 183 protected: 184 virtual RenderViewHost* CreateRenderViewHost() OVERRIDE { 185 return new TestRenderViewHost( 186 SiteInstance::Create(web_contents()->GetBrowserContext()), 187 this, this, MSG_ROUTING_NONE, MSG_ROUTING_NONE, false); 188 } 189 190 virtual WebContentsView* CreateWebContentsView() OVERRIDE { 191 return NULL; 192 } 193 194 private: 195 InterstitialState* state_; 196 bool* deleted_; 197 int command_received_count_; 198 Delegate* delegate_; 199}; 200 201void TestInterstitialPageDelegate::CommandReceived(const std::string& command) { 202 interstitial_page_->CommandReceived(); 203} 204 205void TestInterstitialPageDelegate::OnDontProceed() { 206 interstitial_page_->OnDontProceed(); 207} 208 209void TestInterstitialPageDelegate::OnProceed() { 210 interstitial_page_->OnProceed(); 211} 212 213class TestInterstitialPageStateGuard : public TestInterstitialPage::Delegate { 214 public: 215 explicit TestInterstitialPageStateGuard( 216 TestInterstitialPage* interstitial_page) 217 : interstitial_page_(interstitial_page) { 218 DCHECK(interstitial_page_); 219 interstitial_page_->set_delegate(this); 220 } 221 virtual ~TestInterstitialPageStateGuard() { 222 if (interstitial_page_) 223 interstitial_page_->ClearStates(); 224 } 225 226 virtual void TestInterstitialPageDeleted( 227 TestInterstitialPage* interstitial) OVERRIDE { 228 DCHECK(interstitial_page_ == interstitial); 229 interstitial_page_ = NULL; 230 } 231 232 private: 233 TestInterstitialPage* interstitial_page_; 234}; 235 236class WebContentsImplTest : public RenderViewHostImplTestHarness { 237 public: 238 virtual void SetUp() { 239 RenderViewHostImplTestHarness::SetUp(); 240 WebUIControllerFactory::RegisterFactory(&factory_); 241 } 242 243 virtual void TearDown() { 244 WebUIControllerFactory::UnregisterFactoryForTesting(&factory_); 245 RenderViewHostImplTestHarness::TearDown(); 246 } 247 248 private: 249 WebContentsImplTestWebUIControllerFactory factory_; 250}; 251 252class TestWebContentsObserver : public WebContentsObserver { 253 public: 254 TestWebContentsObserver(WebContents* contents) 255 : WebContentsObserver(contents) { 256 } 257 virtual ~TestWebContentsObserver() {} 258 259 virtual void DidFinishLoad(int64 frame_id, 260 const GURL& validated_url, 261 bool is_main_frame, 262 RenderViewHost* render_view_host) OVERRIDE { 263 last_url_ = validated_url; 264 } 265 virtual void DidFailLoad(int64 frame_id, 266 const GURL& validated_url, 267 bool is_main_frame, 268 int error_code, 269 const string16& error_description, 270 RenderViewHost* render_view_host) OVERRIDE { 271 last_url_ = validated_url; 272 } 273 274 const GURL& last_url() const { return last_url_; } 275 276 private: 277 GURL last_url_; 278 279 DISALLOW_COPY_AND_ASSIGN(TestWebContentsObserver); 280}; 281 282} // namespace 283 284// Test to make sure that title updates get stripped of whitespace. 285TEST_F(WebContentsImplTest, UpdateTitle) { 286 NavigationControllerImpl& cont = 287 static_cast<NavigationControllerImpl&>(controller()); 288 ViewHostMsg_FrameNavigate_Params params; 289 InitNavigateParams(¶ms, 0, GURL(kAboutBlankURL), PAGE_TRANSITION_TYPED); 290 291 LoadCommittedDetails details; 292 cont.RendererDidNavigate(params, &details); 293 294 contents()->UpdateTitle(rvh(), 0, ASCIIToUTF16(" Lots O' Whitespace\n"), 295 base::i18n::LEFT_TO_RIGHT); 296 EXPECT_EQ(ASCIIToUTF16("Lots O' Whitespace"), contents()->GetTitle()); 297} 298 299// Test view source mode for a webui page. 300TEST_F(WebContentsImplTest, NTPViewSource) { 301 NavigationControllerImpl& cont = 302 static_cast<NavigationControllerImpl&>(controller()); 303 const char kUrl[] = "view-source:chrome://blah"; 304 const GURL kGURL(kUrl); 305 306 process()->sink().ClearMessages(); 307 308 cont.LoadURL( 309 kGURL, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 310 rvh()->GetDelegate()->RenderViewCreated(rvh()); 311 // Did we get the expected message? 312 EXPECT_TRUE(process()->sink().GetFirstMessageMatching( 313 ViewMsg_EnableViewSourceMode::ID)); 314 315 ViewHostMsg_FrameNavigate_Params params; 316 InitNavigateParams(¶ms, 0, kGURL, PAGE_TRANSITION_TYPED); 317 LoadCommittedDetails details; 318 cont.RendererDidNavigate(params, &details); 319 // Also check title and url. 320 EXPECT_EQ(ASCIIToUTF16(kUrl), contents()->GetTitle()); 321} 322 323// Test to ensure UpdateMaxPageID is working properly. 324TEST_F(WebContentsImplTest, UpdateMaxPageID) { 325 SiteInstance* instance1 = contents()->GetSiteInstance(); 326 scoped_refptr<SiteInstance> instance2(SiteInstance::Create(NULL)); 327 328 // Starts at -1. 329 EXPECT_EQ(-1, contents()->GetMaxPageID()); 330 EXPECT_EQ(-1, contents()->GetMaxPageIDForSiteInstance(instance1)); 331 EXPECT_EQ(-1, contents()->GetMaxPageIDForSiteInstance(instance2.get())); 332 333 // Make sure max_page_id_ is monotonically increasing per SiteInstance. 334 contents()->UpdateMaxPageID(3); 335 contents()->UpdateMaxPageID(1); 336 EXPECT_EQ(3, contents()->GetMaxPageID()); 337 EXPECT_EQ(3, contents()->GetMaxPageIDForSiteInstance(instance1)); 338 EXPECT_EQ(-1, contents()->GetMaxPageIDForSiteInstance(instance2.get())); 339 340 contents()->UpdateMaxPageIDForSiteInstance(instance2.get(), 7); 341 EXPECT_EQ(3, contents()->GetMaxPageID()); 342 EXPECT_EQ(3, contents()->GetMaxPageIDForSiteInstance(instance1)); 343 EXPECT_EQ(7, contents()->GetMaxPageIDForSiteInstance(instance2.get())); 344} 345 346// Test simple same-SiteInstance navigation. 347TEST_F(WebContentsImplTest, SimpleNavigation) { 348 TestRenderViewHost* orig_rvh = test_rvh(); 349 SiteInstance* instance1 = contents()->GetSiteInstance(); 350 EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); 351 352 // Navigate to URL 353 const GURL url("http://www.google.com"); 354 controller().LoadURL( 355 url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 356 EXPECT_FALSE(contents()->cross_navigation_pending()); 357 EXPECT_EQ(instance1, orig_rvh->GetSiteInstance()); 358 // Controller's pending entry will have a NULL site instance until we assign 359 // it in DidNavigate. 360 EXPECT_TRUE( 361 NavigationEntryImpl::FromNavigationEntry(controller().GetActiveEntry())-> 362 site_instance() == NULL); 363 364 // DidNavigate from the page 365 contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); 366 EXPECT_FALSE(contents()->cross_navigation_pending()); 367 EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); 368 EXPECT_EQ(instance1, orig_rvh->GetSiteInstance()); 369 // Controller's entry should now have the SiteInstance, or else we won't be 370 // able to find it later. 371 EXPECT_EQ( 372 instance1, 373 NavigationEntryImpl::FromNavigationEntry(controller().GetActiveEntry())-> 374 site_instance()); 375} 376 377// Test that we reject NavigateToEntry if the url is over kMaxURLChars. 378TEST_F(WebContentsImplTest, NavigateToExcessivelyLongURL) { 379 // Construct a URL that's kMaxURLChars + 1 long of all 'a's. 380 const GURL url(std::string("http://example.org/").append( 381 kMaxURLChars + 1, 'a')); 382 383 controller().LoadURL( 384 url, Referrer(), PAGE_TRANSITION_GENERATED, std::string()); 385 EXPECT_TRUE(controller().GetActiveEntry() == NULL); 386} 387 388// Test that navigating across a site boundary creates a new RenderViewHost 389// with a new SiteInstance. Going back should do the same. 390TEST_F(WebContentsImplTest, CrossSiteBoundaries) { 391 contents()->transition_cross_site = true; 392 TestRenderViewHost* orig_rvh = test_rvh(); 393 int orig_rvh_delete_count = 0; 394 orig_rvh->set_delete_counter(&orig_rvh_delete_count); 395 SiteInstance* instance1 = contents()->GetSiteInstance(); 396 397 // Navigate to URL. First URL should use first RenderViewHost. 398 const GURL url("http://www.google.com"); 399 controller().LoadURL( 400 url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 401 contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); 402 403 EXPECT_FALSE(contents()->cross_navigation_pending()); 404 EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); 405 EXPECT_EQ(url, contents()->GetLastCommittedURL()); 406 EXPECT_EQ(url, contents()->GetActiveURL()); 407 408 // Navigate to new site 409 const GURL url2("http://www.yahoo.com"); 410 controller().LoadURL( 411 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 412 EXPECT_TRUE(contents()->cross_navigation_pending()); 413 EXPECT_EQ(url, contents()->GetLastCommittedURL()); 414 EXPECT_EQ(url2, contents()->GetActiveURL()); 415 TestRenderViewHost* pending_rvh = 416 static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost()); 417 int pending_rvh_delete_count = 0; 418 pending_rvh->set_delete_counter(&pending_rvh_delete_count); 419 420 // Navigations should be suspended in pending_rvh until ShouldCloseACK. 421 EXPECT_TRUE(pending_rvh->are_navigations_suspended()); 422 orig_rvh->SendShouldCloseACK(true); 423 EXPECT_FALSE(pending_rvh->are_navigations_suspended()); 424 425 // DidNavigate from the pending page 426 contents()->TestDidNavigate( 427 pending_rvh, 1, url2, PAGE_TRANSITION_TYPED); 428 SiteInstance* instance2 = contents()->GetSiteInstance(); 429 430 EXPECT_FALSE(contents()->cross_navigation_pending()); 431 EXPECT_EQ(pending_rvh, contents()->GetRenderViewHost()); 432 EXPECT_EQ(url2, contents()->GetLastCommittedURL()); 433 EXPECT_EQ(url2, contents()->GetActiveURL()); 434 EXPECT_NE(instance1, instance2); 435 EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); 436 // We keep the original RVH around, swapped out. 437 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->IsOnSwappedOutList( 438 orig_rvh)); 439 EXPECT_EQ(orig_rvh_delete_count, 0); 440 441 // Going back should switch SiteInstances again. The first SiteInstance is 442 // stored in the NavigationEntry, so it should be the same as at the start. 443 // We should use the same RVH as before, swapping it back in. 444 controller().GoBack(); 445 TestRenderViewHost* goback_rvh = 446 static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost()); 447 EXPECT_EQ(orig_rvh, goback_rvh); 448 EXPECT_TRUE(contents()->cross_navigation_pending()); 449 450 // Navigations should be suspended in goback_rvh until ShouldCloseACK. 451 EXPECT_TRUE(goback_rvh->are_navigations_suspended()); 452 pending_rvh->SendShouldCloseACK(true); 453 EXPECT_FALSE(goback_rvh->are_navigations_suspended()); 454 455 // DidNavigate from the back action 456 contents()->TestDidNavigate( 457 goback_rvh, 1, url2, PAGE_TRANSITION_TYPED); 458 EXPECT_FALSE(contents()->cross_navigation_pending()); 459 EXPECT_EQ(goback_rvh, contents()->GetRenderViewHost()); 460 EXPECT_EQ(instance1, contents()->GetSiteInstance()); 461 // The pending RVH should now be swapped out, not deleted. 462 EXPECT_TRUE(contents()->GetRenderManagerForTesting()-> 463 IsOnSwappedOutList(pending_rvh)); 464 EXPECT_EQ(pending_rvh_delete_count, 0); 465 466 // Close contents and ensure RVHs are deleted. 467 DeleteContents(); 468 EXPECT_EQ(orig_rvh_delete_count, 1); 469 EXPECT_EQ(pending_rvh_delete_count, 1); 470} 471 472// Test that navigating across a site boundary after a crash creates a new 473// RVH without requiring a cross-site transition (i.e., PENDING state). 474TEST_F(WebContentsImplTest, CrossSiteBoundariesAfterCrash) { 475 contents()->transition_cross_site = true; 476 TestRenderViewHost* orig_rvh = test_rvh(); 477 int orig_rvh_delete_count = 0; 478 orig_rvh->set_delete_counter(&orig_rvh_delete_count); 479 SiteInstance* instance1 = contents()->GetSiteInstance(); 480 481 // Navigate to URL. First URL should use first RenderViewHost. 482 const GURL url("http://www.google.com"); 483 controller().LoadURL( 484 url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 485 contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); 486 487 EXPECT_FALSE(contents()->cross_navigation_pending()); 488 EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); 489 490 // Crash the renderer. 491 orig_rvh->set_render_view_created(false); 492 493 // Navigate to new site. We should not go into PENDING. 494 const GURL url2("http://www.yahoo.com"); 495 controller().LoadURL( 496 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 497 RenderViewHost* new_rvh = rvh(); 498 EXPECT_FALSE(contents()->cross_navigation_pending()); 499 EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); 500 EXPECT_NE(orig_rvh, new_rvh); 501 EXPECT_EQ(orig_rvh_delete_count, 1); 502 503 // DidNavigate from the new page 504 contents()->TestDidNavigate(new_rvh, 1, url2, PAGE_TRANSITION_TYPED); 505 SiteInstance* instance2 = contents()->GetSiteInstance(); 506 507 EXPECT_FALSE(contents()->cross_navigation_pending()); 508 EXPECT_EQ(new_rvh, rvh()); 509 EXPECT_NE(instance1, instance2); 510 EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); 511 512 // Close contents and ensure RVHs are deleted. 513 DeleteContents(); 514 EXPECT_EQ(orig_rvh_delete_count, 1); 515} 516 517// Test that opening a new contents in the same SiteInstance and then navigating 518// both contentses to a new site will place both contentses in a single 519// SiteInstance. 520TEST_F(WebContentsImplTest, NavigateTwoTabsCrossSite) { 521 contents()->transition_cross_site = true; 522 TestRenderViewHost* orig_rvh = test_rvh(); 523 SiteInstance* instance1 = contents()->GetSiteInstance(); 524 525 // Navigate to URL. First URL should use first RenderViewHost. 526 const GURL url("http://www.google.com"); 527 controller().LoadURL( 528 url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 529 contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); 530 531 // Open a new contents with the same SiteInstance, navigated to the same site. 532 scoped_ptr<TestWebContents> contents2( 533 TestWebContents::Create(browser_context_.get(), instance1)); 534 contents2->transition_cross_site = true; 535 contents2->GetController().LoadURL(url, Referrer(), 536 PAGE_TRANSITION_TYPED, 537 std::string()); 538 // Need this page id to be 2 since the site instance is the same (which is the 539 // scope of page IDs) and we want to consider this a new page. 540 contents2->TestDidNavigate( 541 contents2->GetRenderViewHost(), 2, url, PAGE_TRANSITION_TYPED); 542 543 // Navigate first contents to a new site. 544 const GURL url2a("http://www.yahoo.com"); 545 controller().LoadURL( 546 url2a, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 547 orig_rvh->SendShouldCloseACK(true); 548 TestRenderViewHost* pending_rvh_a = 549 static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost()); 550 contents()->TestDidNavigate( 551 pending_rvh_a, 1, url2a, PAGE_TRANSITION_TYPED); 552 SiteInstance* instance2a = contents()->GetSiteInstance(); 553 EXPECT_NE(instance1, instance2a); 554 555 // Navigate second contents to the same site as the first tab. 556 const GURL url2b("http://mail.yahoo.com"); 557 contents2->GetController().LoadURL(url2b, Referrer(), 558 PAGE_TRANSITION_TYPED, 559 std::string()); 560 TestRenderViewHost* rvh2 = 561 static_cast<TestRenderViewHost*>(contents2->GetRenderViewHost()); 562 rvh2->SendShouldCloseACK(true); 563 TestRenderViewHost* pending_rvh_b = 564 static_cast<TestRenderViewHost*>(contents2->GetPendingRenderViewHost()); 565 EXPECT_TRUE(pending_rvh_b != NULL); 566 EXPECT_TRUE(contents2->cross_navigation_pending()); 567 568 // NOTE(creis): We used to be in danger of showing a crash page here if the 569 // second contents hadn't navigated somewhere first (bug 1145430). That case 570 // is now covered by the CrossSiteBoundariesAfterCrash test. 571 contents2->TestDidNavigate( 572 pending_rvh_b, 2, url2b, PAGE_TRANSITION_TYPED); 573 SiteInstance* instance2b = contents2->GetSiteInstance(); 574 EXPECT_NE(instance1, instance2b); 575 576 // Both contentses should now be in the same SiteInstance. 577 EXPECT_EQ(instance2a, instance2b); 578} 579 580// Test that we can find an opener RVH even if it's pending. 581// http://crbug.com/176252. 582TEST_F(WebContentsImplTest, FindOpenerRVHWhenPending) { 583 contents()->transition_cross_site = true; 584 TestRenderViewHost* orig_rvh = test_rvh(); 585 586 // Navigate to a URL. 587 const GURL url("http://www.google.com"); 588 controller().LoadURL( 589 url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 590 contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); 591 592 // Start to navigate first tab to a new site, so that it has a pending RVH. 593 const GURL url2("http://www.yahoo.com"); 594 controller().LoadURL( 595 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 596 orig_rvh->SendShouldCloseACK(true); 597 TestRenderViewHost* pending_rvh = 598 static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost()); 599 600 // While it is still pending, simulate opening a new tab with the first tab 601 // as its opener. This will call WebContentsImpl::CreateOpenerRenderViews 602 // on the opener to ensure that an RVH exists. 603 int opener_routing_id = contents()->CreateOpenerRenderViews( 604 pending_rvh->GetSiteInstance()); 605 606 // We should find the pending RVH and not create a new one. 607 EXPECT_EQ(pending_rvh->GetRoutingID(), opener_routing_id); 608} 609 610// Tests that WebContentsImpl uses the current URL, not the SiteInstance's site, 611// to determine whether a navigation is cross-site. 612TEST_F(WebContentsImplTest, CrossSiteComparesAgainstCurrentPage) { 613 contents()->transition_cross_site = true; 614 RenderViewHost* orig_rvh = rvh(); 615 SiteInstance* instance1 = contents()->GetSiteInstance(); 616 617 // Navigate to URL. 618 const GURL url("http://www.google.com"); 619 controller().LoadURL( 620 url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 621 contents()->TestDidNavigate( 622 orig_rvh, 1, url, PAGE_TRANSITION_TYPED); 623 624 // Open a related contents to a second site. 625 scoped_ptr<TestWebContents> contents2( 626 TestWebContents::Create(browser_context_.get(), instance1)); 627 contents2->transition_cross_site = true; 628 const GURL url2("http://www.yahoo.com"); 629 contents2->GetController().LoadURL(url2, Referrer(), 630 PAGE_TRANSITION_TYPED, 631 std::string()); 632 // The first RVH in contents2 isn't live yet, so we shortcut the cross site 633 // pending. 634 TestRenderViewHost* rvh2 = static_cast<TestRenderViewHost*>( 635 contents2->GetRenderViewHost()); 636 EXPECT_FALSE(contents2->cross_navigation_pending()); 637 contents2->TestDidNavigate(rvh2, 2, url2, PAGE_TRANSITION_TYPED); 638 SiteInstance* instance2 = contents2->GetSiteInstance(); 639 EXPECT_NE(instance1, instance2); 640 EXPECT_FALSE(contents2->cross_navigation_pending()); 641 642 // Simulate a link click in first contents to second site. Doesn't switch 643 // SiteInstances, because we don't intercept WebKit navigations. 644 contents()->TestDidNavigate( 645 orig_rvh, 2, url2, PAGE_TRANSITION_TYPED); 646 SiteInstance* instance3 = contents()->GetSiteInstance(); 647 EXPECT_EQ(instance1, instance3); 648 EXPECT_FALSE(contents()->cross_navigation_pending()); 649 650 // Navigate to the new site. Doesn't switch SiteInstancees, because we 651 // compare against the current URL, not the SiteInstance's site. 652 const GURL url3("http://mail.yahoo.com"); 653 controller().LoadURL( 654 url3, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 655 EXPECT_FALSE(contents()->cross_navigation_pending()); 656 contents()->TestDidNavigate( 657 orig_rvh, 3, url3, PAGE_TRANSITION_TYPED); 658 SiteInstance* instance4 = contents()->GetSiteInstance(); 659 EXPECT_EQ(instance1, instance4); 660} 661 662// Test that the onbeforeunload and onunload handlers run when navigating 663// across site boundaries. 664TEST_F(WebContentsImplTest, CrossSiteUnloadHandlers) { 665 contents()->transition_cross_site = true; 666 TestRenderViewHost* orig_rvh = test_rvh(); 667 SiteInstance* instance1 = contents()->GetSiteInstance(); 668 669 // Navigate to URL. First URL should use first RenderViewHost. 670 const GURL url("http://www.google.com"); 671 controller().LoadURL( 672 url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 673 contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); 674 EXPECT_FALSE(contents()->cross_navigation_pending()); 675 EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); 676 677 // Navigate to new site, but simulate an onbeforeunload denial. 678 const GURL url2("http://www.yahoo.com"); 679 controller().LoadURL( 680 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 681 EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack()); 682 base::TimeTicks now = base::TimeTicks::Now(); 683 orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, false, now, now)); 684 EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack()); 685 EXPECT_FALSE(contents()->cross_navigation_pending()); 686 EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); 687 688 // Navigate again, but simulate an onbeforeunload approval. 689 controller().LoadURL( 690 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 691 EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack()); 692 now = base::TimeTicks::Now(); 693 orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now)); 694 EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack()); 695 EXPECT_TRUE(contents()->cross_navigation_pending()); 696 TestRenderViewHost* pending_rvh = static_cast<TestRenderViewHost*>( 697 contents()->GetPendingRenderViewHost()); 698 699 // We won't hear DidNavigate until the onunload handler has finished running. 700 // (No way to simulate that here, but it involves a call from RDH to 701 // WebContentsImpl::OnCrossSiteResponse.) 702 703 // DidNavigate from the pending page 704 contents()->TestDidNavigate( 705 pending_rvh, 1, url2, PAGE_TRANSITION_TYPED); 706 SiteInstance* instance2 = contents()->GetSiteInstance(); 707 EXPECT_FALSE(contents()->cross_navigation_pending()); 708 EXPECT_EQ(pending_rvh, rvh()); 709 EXPECT_NE(instance1, instance2); 710 EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); 711} 712 713// Test that during a slow cross-site navigation, the original renderer can 714// navigate to a different URL and have it displayed, canceling the slow 715// navigation. 716TEST_F(WebContentsImplTest, CrossSiteNavigationPreempted) { 717 contents()->transition_cross_site = true; 718 TestRenderViewHost* orig_rvh = test_rvh(); 719 SiteInstance* instance1 = contents()->GetSiteInstance(); 720 721 // Navigate to URL. First URL should use first RenderViewHost. 722 const GURL url("http://www.google.com"); 723 controller().LoadURL( 724 url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 725 contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); 726 EXPECT_FALSE(contents()->cross_navigation_pending()); 727 EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); 728 729 // Navigate to new site, simulating an onbeforeunload approval. 730 const GURL url2("http://www.yahoo.com"); 731 controller().LoadURL( 732 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 733 EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack()); 734 base::TimeTicks now = base::TimeTicks::Now(); 735 orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now)); 736 EXPECT_TRUE(contents()->cross_navigation_pending()); 737 738 // Suppose the original renderer navigates before the new one is ready. 739 orig_rvh->SendNavigate(2, GURL("http://www.google.com/foo")); 740 741 // Verify that the pending navigation is cancelled. 742 EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack()); 743 SiteInstance* instance2 = contents()->GetSiteInstance(); 744 EXPECT_FALSE(contents()->cross_navigation_pending()); 745 EXPECT_EQ(orig_rvh, rvh()); 746 EXPECT_EQ(instance1, instance2); 747 EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); 748} 749 750TEST_F(WebContentsImplTest, CrossSiteNavigationBackPreempted) { 751 contents()->transition_cross_site = true; 752 753 // Start with a web ui page, which gets a new RVH with WebUI bindings. 754 const GURL url1("chrome://blah"); 755 controller().LoadURL( 756 url1, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 757 TestRenderViewHost* ntp_rvh = test_rvh(); 758 contents()->TestDidNavigate(ntp_rvh, 1, url1, PAGE_TRANSITION_TYPED); 759 NavigationEntry* entry1 = controller().GetLastCommittedEntry(); 760 SiteInstance* instance1 = contents()->GetSiteInstance(); 761 762 EXPECT_FALSE(contents()->cross_navigation_pending()); 763 EXPECT_EQ(ntp_rvh, contents()->GetRenderViewHost()); 764 EXPECT_EQ(url1, entry1->GetURL()); 765 EXPECT_EQ(instance1, 766 NavigationEntryImpl::FromNavigationEntry(entry1)->site_instance()); 767 EXPECT_TRUE(ntp_rvh->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI); 768 769 // Navigate to new site. 770 const GURL url2("http://www.google.com"); 771 controller().LoadURL( 772 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 773 EXPECT_TRUE(contents()->cross_navigation_pending()); 774 TestRenderViewHost* google_rvh = 775 static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost()); 776 777 // Simulate beforeunload approval. 778 EXPECT_TRUE(ntp_rvh->is_waiting_for_beforeunload_ack()); 779 base::TimeTicks now = base::TimeTicks::Now(); 780 ntp_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now)); 781 782 // DidNavigate from the pending page. 783 contents()->TestDidNavigate( 784 google_rvh, 1, url2, PAGE_TRANSITION_TYPED); 785 NavigationEntry* entry2 = controller().GetLastCommittedEntry(); 786 SiteInstance* instance2 = contents()->GetSiteInstance(); 787 788 EXPECT_FALSE(contents()->cross_navigation_pending()); 789 EXPECT_EQ(google_rvh, contents()->GetRenderViewHost()); 790 EXPECT_NE(instance1, instance2); 791 EXPECT_FALSE(contents()->GetPendingRenderViewHost()); 792 EXPECT_EQ(url2, entry2->GetURL()); 793 EXPECT_EQ(instance2, 794 NavigationEntryImpl::FromNavigationEntry(entry2)->site_instance()); 795 EXPECT_FALSE(google_rvh->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI); 796 797 // Navigate to third page on same site. 798 const GURL url3("http://news.google.com"); 799 controller().LoadURL( 800 url3, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 801 EXPECT_FALSE(contents()->cross_navigation_pending()); 802 contents()->TestDidNavigate( 803 google_rvh, 2, url3, PAGE_TRANSITION_TYPED); 804 NavigationEntry* entry3 = controller().GetLastCommittedEntry(); 805 SiteInstance* instance3 = contents()->GetSiteInstance(); 806 807 EXPECT_FALSE(contents()->cross_navigation_pending()); 808 EXPECT_EQ(google_rvh, contents()->GetRenderViewHost()); 809 EXPECT_EQ(instance2, instance3); 810 EXPECT_FALSE(contents()->GetPendingRenderViewHost()); 811 EXPECT_EQ(url3, entry3->GetURL()); 812 EXPECT_EQ(instance3, 813 NavigationEntryImpl::FromNavigationEntry(entry3)->site_instance()); 814 815 // Go back within the site. 816 controller().GoBack(); 817 EXPECT_FALSE(contents()->cross_navigation_pending()); 818 EXPECT_EQ(entry2, controller().GetPendingEntry()); 819 820 // Before that commits, go back again. 821 controller().GoBack(); 822 EXPECT_TRUE(contents()->cross_navigation_pending()); 823 EXPECT_TRUE(contents()->GetPendingRenderViewHost()); 824 EXPECT_EQ(entry1, controller().GetPendingEntry()); 825 826 // Simulate beforeunload approval. 827 EXPECT_TRUE(google_rvh->is_waiting_for_beforeunload_ack()); 828 now = base::TimeTicks::Now(); 829 google_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now)); 830 831 // DidNavigate from the first back. This aborts the second back's pending RVH. 832 contents()->TestDidNavigate(google_rvh, 1, url2, PAGE_TRANSITION_TYPED); 833 834 // We should commit this page and forget about the second back. 835 EXPECT_FALSE(contents()->cross_navigation_pending()); 836 EXPECT_FALSE(controller().GetPendingEntry()); 837 EXPECT_EQ(google_rvh, contents()->GetRenderViewHost()); 838 EXPECT_EQ(url2, controller().GetLastCommittedEntry()->GetURL()); 839 840 // We should not have corrupted the NTP entry. 841 EXPECT_EQ(instance3, 842 NavigationEntryImpl::FromNavigationEntry(entry3)->site_instance()); 843 EXPECT_EQ(instance2, 844 NavigationEntryImpl::FromNavigationEntry(entry2)->site_instance()); 845 EXPECT_EQ(instance1, 846 NavigationEntryImpl::FromNavigationEntry(entry1)->site_instance()); 847 EXPECT_EQ(url1, entry1->GetURL()); 848} 849 850// Test that during a slow cross-site navigation, a sub-frame navigation in the 851// original renderer will not cancel the slow navigation (bug 42029). 852TEST_F(WebContentsImplTest, CrossSiteNavigationNotPreemptedByFrame) { 853 contents()->transition_cross_site = true; 854 TestRenderViewHost* orig_rvh = test_rvh(); 855 856 // Navigate to URL. First URL should use first RenderViewHost. 857 const GURL url("http://www.google.com"); 858 controller().LoadURL( 859 url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 860 contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); 861 EXPECT_FALSE(contents()->cross_navigation_pending()); 862 EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); 863 864 // Start navigating to new site. 865 const GURL url2("http://www.yahoo.com"); 866 controller().LoadURL( 867 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 868 869 // Simulate a sub-frame navigation arriving and ensure the RVH is still 870 // waiting for a before unload response. 871 orig_rvh->SendNavigateWithTransition(1, GURL("http://google.com/frame"), 872 PAGE_TRANSITION_AUTO_SUBFRAME); 873 EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack()); 874 875 // Now simulate the onbeforeunload approval and verify the navigation is 876 // not canceled. 877 base::TimeTicks now = base::TimeTicks::Now(); 878 orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now)); 879 EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack()); 880 EXPECT_TRUE(contents()->cross_navigation_pending()); 881} 882 883// Test that a cross-site navigation is not preempted if the previous 884// renderer sends a FrameNavigate message just before being told to stop. 885// We should only preempt the cross-site navigation if the previous renderer 886// has started a new navigation. See http://crbug.com/79176. 887TEST_F(WebContentsImplTest, CrossSiteNotPreemptedDuringBeforeUnload) { 888 contents()->transition_cross_site = true; 889 890 // Navigate to NTP URL. 891 const GURL url("chrome://blah"); 892 controller().LoadURL( 893 url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 894 TestRenderViewHost* orig_rvh = test_rvh(); 895 EXPECT_FALSE(contents()->cross_navigation_pending()); 896 897 // Navigate to new site, with the beforeunload request in flight. 898 const GURL url2("http://www.yahoo.com"); 899 controller().LoadURL( 900 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 901 TestRenderViewHost* pending_rvh = 902 static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost()); 903 EXPECT_TRUE(contents()->cross_navigation_pending()); 904 EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack()); 905 906 // Suppose the first navigation tries to commit now, with a 907 // ViewMsg_Stop in flight. This should not cancel the pending navigation, 908 // but it should act as if the beforeunload ack arrived. 909 orig_rvh->SendNavigate(1, GURL("chrome://blah")); 910 EXPECT_TRUE(contents()->cross_navigation_pending()); 911 EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); 912 EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack()); 913 914 // The pending navigation should be able to commit successfully. 915 contents()->TestDidNavigate(pending_rvh, 1, url2, PAGE_TRANSITION_TYPED); 916 EXPECT_FALSE(contents()->cross_navigation_pending()); 917 EXPECT_EQ(pending_rvh, contents()->GetRenderViewHost()); 918} 919 920// Test that the original renderer cannot preempt a cross-site navigation once 921// the unload request has been made. At this point, the cross-site navigation 922// is almost ready to be displayed, and the original renderer is only given a 923// short chance to run an unload handler. Prevents regression of bug 23942. 924TEST_F(WebContentsImplTest, CrossSiteCantPreemptAfterUnload) { 925 contents()->transition_cross_site = true; 926 TestRenderViewHost* orig_rvh = test_rvh(); 927 SiteInstance* instance1 = contents()->GetSiteInstance(); 928 929 // Navigate to URL. First URL should use first RenderViewHost. 930 const GURL url("http://www.google.com"); 931 controller().LoadURL( 932 url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 933 contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); 934 EXPECT_FALSE(contents()->cross_navigation_pending()); 935 EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); 936 937 // Navigate to new site, simulating an onbeforeunload approval. 938 const GURL url2("http://www.yahoo.com"); 939 controller().LoadURL( 940 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 941 base::TimeTicks now = base::TimeTicks::Now(); 942 orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now)); 943 EXPECT_TRUE(contents()->cross_navigation_pending()); 944 TestRenderViewHost* pending_rvh = static_cast<TestRenderViewHost*>( 945 contents()->GetPendingRenderViewHost()); 946 947 // Simulate the pending renderer's response, which leads to an unload request 948 // being sent to orig_rvh. 949 contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(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