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