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