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