render_frame_host_manager_unittest.cc revision a02191e04bc25c4935f804f2c080ae28663d096d
1// Copyright 2013 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/files/file_path.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/navigation_controller_impl.h" 9#include "content/browser/frame_host/navigation_entry_impl.h" 10#include "content/browser/frame_host/navigator.h" 11#include "content/browser/frame_host/render_frame_host_manager.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/view_messages.h" 16#include "content/public/browser/notification_details.h" 17#include "content/public/browser/notification_service.h" 18#include "content/public/browser/notification_source.h" 19#include "content/public/browser/notification_types.h" 20#include "content/public/browser/render_process_host.h" 21#include "content/public/browser/render_widget_host_iterator.h" 22#include "content/public/browser/web_contents_delegate.h" 23#include "content/public/browser/web_contents_observer.h" 24#include "content/public/browser/web_ui_controller.h" 25#include "content/public/common/bindings_policy.h" 26#include "content/public/common/javascript_message_type.h" 27#include "content/public/common/page_transition_types.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_notification_tracker.h" 32#include "content/test/test_content_browser_client.h" 33#include "content/test/test_content_client.h" 34#include "content/test/test_render_view_host.h" 35#include "content/test/test_web_contents.h" 36#include "testing/gtest/include/gtest/gtest.h" 37 38namespace content { 39namespace { 40 41class RenderFrameHostManagerTestWebUIControllerFactory 42 : public WebUIControllerFactory { 43 public: 44 RenderFrameHostManagerTestWebUIControllerFactory() 45 : should_create_webui_(false) { 46 } 47 virtual ~RenderFrameHostManagerTestWebUIControllerFactory() {} 48 49 void set_should_create_webui(bool should_create_webui) { 50 should_create_webui_ = should_create_webui; 51 } 52 53 // WebUIFactory implementation. 54 virtual WebUIController* CreateWebUIControllerForURL( 55 WebUI* web_ui, const GURL& url) const OVERRIDE { 56 if (!(should_create_webui_ && HasWebUIScheme(url))) 57 return NULL; 58 return new WebUIController(web_ui); 59 } 60 61 virtual WebUI::TypeID GetWebUIType(BrowserContext* browser_context, 62 const GURL& url) const OVERRIDE { 63 return WebUI::kNoWebUI; 64 } 65 66 virtual bool UseWebUIForURL(BrowserContext* browser_context, 67 const GURL& url) const OVERRIDE { 68 return HasWebUIScheme(url); 69 } 70 71 virtual bool UseWebUIBindingsForURL(BrowserContext* browser_context, 72 const GURL& url) const OVERRIDE { 73 return HasWebUIScheme(url); 74 } 75 76 private: 77 bool should_create_webui_; 78 79 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostManagerTestWebUIControllerFactory); 80}; 81 82class BeforeUnloadFiredWebContentsDelegate : public WebContentsDelegate { 83 public: 84 BeforeUnloadFiredWebContentsDelegate() {} 85 virtual ~BeforeUnloadFiredWebContentsDelegate() {} 86 87 virtual void BeforeUnloadFired(WebContents* web_contents, 88 bool proceed, 89 bool* proceed_to_fire_unload) OVERRIDE { 90 *proceed_to_fire_unload = proceed; 91 } 92 93 private: 94 DISALLOW_COPY_AND_ASSIGN(BeforeUnloadFiredWebContentsDelegate); 95}; 96 97// This observer keeps track of the last deleted RenderViewHost to avoid 98// accessing it and causing use-after-free condition. 99class RenderViewHostDeletedObserver : public WebContentsObserver { 100 public: 101 RenderViewHostDeletedObserver(RenderViewHost* rvh) 102 : WebContentsObserver(WebContents::FromRenderViewHost(rvh)), 103 process_id_(rvh->GetProcess()->GetID()), 104 routing_id_(rvh->GetRoutingID()), 105 deleted_(false) { 106 } 107 108 virtual void RenderViewDeleted(RenderViewHost* render_view_host) OVERRIDE { 109 if (render_view_host->GetProcess()->GetID() == process_id_ && 110 render_view_host->GetRoutingID() == routing_id_) { 111 deleted_ = true; 112 } 113 } 114 115 bool deleted() { 116 return deleted_; 117 } 118 119 private: 120 int process_id_; 121 int routing_id_; 122 bool deleted_; 123 124 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDeletedObserver); 125}; 126 127 128// This observer keeps track of the last deleted RenderViewHost to avoid 129// accessing it and causing use-after-free condition. 130class RenderFrameHostDeletedObserver : public WebContentsObserver { 131 public: 132 RenderFrameHostDeletedObserver(RenderFrameHost* rfh) 133 : WebContentsObserver(WebContents::FromRenderFrameHost(rfh)), 134 process_id_(rfh->GetProcess()->GetID()), 135 routing_id_(rfh->GetRoutingID()), 136 deleted_(false) { 137 } 138 139 virtual void RenderFrameDeleted(RenderFrameHost* render_frame_host) OVERRIDE { 140 if (render_frame_host->GetProcess()->GetID() == process_id_ && 141 render_frame_host->GetRoutingID() == routing_id_) { 142 deleted_ = true; 143 } 144 } 145 146 bool deleted() { 147 return deleted_; 148 } 149 150 private: 151 int process_id_; 152 int routing_id_; 153 bool deleted_; 154 155 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostDeletedObserver); 156}; 157 158 159// This observer is used to check whether IPC messages are being filtered for 160// swapped out RenderFrameHost objects. It observes the plugin crash and favicon 161// update events, which the FilterMessagesWhileSwappedOut test simulates being 162// sent. The test is successful if the event is not observed. 163// See http://crbug.com/351815 164class PluginFaviconMessageObserver : public WebContentsObserver { 165 public: 166 PluginFaviconMessageObserver(WebContents* web_contents) 167 : WebContentsObserver(web_contents), 168 plugin_crashed_(false), 169 favicon_received_(false) { } 170 171 virtual void PluginCrashed(const base::FilePath& plugin_path, 172 base::ProcessId plugin_pid) OVERRIDE { 173 plugin_crashed_ = true; 174 } 175 176 virtual void DidUpdateFaviconURL( 177 int32 page_id, 178 const std::vector<FaviconURL>& candidates) OVERRIDE { 179 favicon_received_ = true; 180 } 181 182 bool plugin_crashed() { 183 return plugin_crashed_; 184 } 185 186 bool favicon_received() { 187 return favicon_received_; 188 } 189 190 private: 191 bool plugin_crashed_; 192 bool favicon_received_; 193 194 DISALLOW_COPY_AND_ASSIGN(PluginFaviconMessageObserver); 195}; 196 197 198} // namespace 199 200class RenderFrameHostManagerTest 201 : public RenderViewHostImplTestHarness { 202 public: 203 virtual void SetUp() OVERRIDE { 204 RenderViewHostImplTestHarness::SetUp(); 205 WebUIControllerFactory::RegisterFactory(&factory_); 206 } 207 208 virtual void TearDown() OVERRIDE { 209 RenderViewHostImplTestHarness::TearDown(); 210 WebUIControllerFactory::UnregisterFactoryForTesting(&factory_); 211 } 212 213 void set_should_create_webui(bool should_create_webui) { 214 factory_.set_should_create_webui(should_create_webui); 215 } 216 217 void StartCrossSiteTransition(TestWebContents* contents) { 218 std::vector<GURL> url_chain; 219 contents->GetRenderManagerForTesting()->OnCrossSiteResponse( 220 contents->GetRenderManagerForTesting()->pending_frame_host(), 221 GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(), 222 url_chain, Referrer(), PAGE_TRANSITION_TYPED, false); 223 EXPECT_TRUE(contents->cross_navigation_pending()); 224 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( 225 contents->GetRenderViewHost()); 226 EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK, 227 rvh->rvh_state()); 228 } 229 230 void NavigateActiveAndCommit(const GURL& url) { 231 // Note: we navigate the active RenderViewHost because previous navigations 232 // won't have committed yet, so NavigateAndCommit does the wrong thing 233 // for us. 234 controller().LoadURL(url, Referrer(), PAGE_TRANSITION_LINK, std::string()); 235 TestRenderViewHost* old_rvh = test_rvh(); 236 237 // Simulate the BeforeUnload_ACK that is received from the current renderer 238 // for a cross-site navigation. 239 if (old_rvh != active_rvh()) { 240 old_rvh->SendBeforeUnloadACK(true); 241 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, old_rvh->rvh_state()); 242 } 243 244 // Commit the navigation with a new page ID. 245 int32 max_page_id = contents()->GetMaxPageIDForSiteInstance( 246 active_rvh()->GetSiteInstance()); 247 248 // Simulate the response coming from the pending renderer. 249 if (old_rvh != active_rvh()) 250 StartCrossSiteTransition(contents()); 251 252 // Simulate the SwapOut_ACK that fires if you commit a cross-site 253 // navigation. 254 if (old_rvh != active_rvh()) { 255 old_rvh->OnSwappedOut(false); 256 EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT, 257 old_rvh->rvh_state()); 258 } 259 260 // Use an observer to avoid accessing a deleted renderer later on when the 261 // state is being checked. 262 RenderViewHostDeletedObserver rvh_observer(old_rvh); 263 active_test_rvh()->SendNavigate(max_page_id + 1, url); 264 265 if (old_rvh != active_rvh() && !rvh_observer.deleted()) 266 EXPECT_TRUE(old_rvh->IsSwappedOut()); 267 } 268 269 bool ShouldSwapProcesses(RenderFrameHostManager* manager, 270 const NavigationEntryImpl* current_entry, 271 const NavigationEntryImpl* new_entry) const { 272 return manager->ShouldSwapBrowsingInstancesForNavigation(current_entry, 273 new_entry); 274 } 275 276 // Creates a test RenderViewHost that's swapped out. 277 TestRenderViewHost* CreateSwappedOutRenderViewHost() { 278 const GURL kChromeURL("chrome://foo"); 279 const GURL kDestUrl("http://www.google.com/"); 280 281 // Navigate our first tab to a chrome url and then to the destination. 282 NavigateActiveAndCommit(kChromeURL); 283 TestRenderViewHost* ntp_rvh = static_cast<TestRenderViewHost*>( 284 contents()->GetRenderManagerForTesting()->current_host()); 285 286 // Navigate to a cross-site URL. 287 contents()->GetController().LoadURL( 288 kDestUrl, Referrer(), PAGE_TRANSITION_LINK, std::string()); 289 EXPECT_TRUE(contents()->cross_navigation_pending()); 290 291 // Manually increase the number of active views in the 292 // SiteInstance that ntp_rvh belongs to, to prevent it from being 293 // destroyed when it gets swapped out. 294 static_cast<SiteInstanceImpl*>(ntp_rvh->GetSiteInstance())-> 295 increment_active_view_count(); 296 297 TestRenderViewHost* dest_rvh = static_cast<TestRenderViewHost*>( 298 contents()->GetRenderManagerForTesting()->pending_render_view_host()); 299 CHECK(dest_rvh); 300 EXPECT_NE(ntp_rvh, dest_rvh); 301 302 // BeforeUnload finishes. 303 ntp_rvh->SendBeforeUnloadACK(true); 304 305 dest_rvh->SendNavigate(101, kDestUrl); 306 ntp_rvh->OnSwappedOut(false); 307 308 EXPECT_TRUE(ntp_rvh->IsSwappedOut()); 309 return ntp_rvh; 310 } 311 312 private: 313 RenderFrameHostManagerTestWebUIControllerFactory factory_; 314}; 315 316// Tests that when you navigate from a chrome:// url to another page, and 317// then do that same thing in another tab, that the two resulting pages have 318// different SiteInstances, BrowsingInstances, and RenderProcessHosts. This is 319// a regression test for bug 9364. 320TEST_F(RenderFrameHostManagerTest, NewTabPageProcesses) { 321 set_should_create_webui(true); 322 const GURL kChromeUrl("chrome://foo"); 323 const GURL kDestUrl("http://www.google.com/"); 324 325 // Navigate our first tab to the chrome url and then to the destination, 326 // ensuring we grant bindings to the chrome URL. 327 NavigateActiveAndCommit(kChromeUrl); 328 EXPECT_TRUE(active_rvh()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI); 329 NavigateActiveAndCommit(kDestUrl); 330 331 // Make a second tab. 332 scoped_ptr<TestWebContents> contents2( 333 TestWebContents::Create(browser_context(), NULL)); 334 335 // Load the two URLs in the second tab. Note that the first navigation creates 336 // a RVH that's not pending (since there is no cross-site transition), so 337 // we use the committed one. 338 contents2->GetController().LoadURL( 339 kChromeUrl, Referrer(), PAGE_TRANSITION_LINK, std::string()); 340 TestRenderViewHost* ntp_rvh2 = static_cast<TestRenderViewHost*>( 341 contents2->GetRenderManagerForTesting()->current_host()); 342 EXPECT_FALSE(contents2->cross_navigation_pending()); 343 ntp_rvh2->SendNavigate(100, kChromeUrl); 344 345 // The second one is the opposite, creating a cross-site transition and 346 // requiring a beforeunload ack. 347 contents2->GetController().LoadURL( 348 kDestUrl, Referrer(), PAGE_TRANSITION_LINK, std::string()); 349 EXPECT_TRUE(contents2->cross_navigation_pending()); 350 TestRenderViewHost* dest_rvh2 = static_cast<TestRenderViewHost*>( 351 contents2->GetRenderManagerForTesting()->pending_render_view_host()); 352 ASSERT_TRUE(dest_rvh2); 353 354 ntp_rvh2->SendBeforeUnloadACK(true); 355 StartCrossSiteTransition(contents2.get()); 356 dest_rvh2->SendNavigate(101, kDestUrl); 357 358 // The two RVH's should be different in every way. 359 EXPECT_NE(active_rvh()->GetProcess(), dest_rvh2->GetProcess()); 360 EXPECT_NE(active_rvh()->GetSiteInstance(), dest_rvh2->GetSiteInstance()); 361 EXPECT_FALSE(active_rvh()->GetSiteInstance()->IsRelatedSiteInstance( 362 dest_rvh2->GetSiteInstance())); 363 364 // Navigate both to the new tab page, and verify that they share a 365 // RenderProcessHost (not a SiteInstance). 366 NavigateActiveAndCommit(kChromeUrl); 367 368 contents2->GetController().LoadURL( 369 kChromeUrl, Referrer(), PAGE_TRANSITION_LINK, std::string()); 370 dest_rvh2->SendBeforeUnloadACK(true); 371 StartCrossSiteTransition(contents2.get()); 372 static_cast<TestRenderViewHost*>(contents2->GetRenderManagerForTesting()-> 373 pending_render_view_host())->SendNavigate(102, kChromeUrl); 374 375 EXPECT_NE(active_rvh()->GetSiteInstance(), 376 contents2->GetRenderViewHost()->GetSiteInstance()); 377 EXPECT_EQ(active_rvh()->GetSiteInstance()->GetProcess(), 378 contents2->GetRenderViewHost()->GetSiteInstance()->GetProcess()); 379} 380 381// Ensure that the browser ignores most IPC messages that arrive from a 382// RenderViewHost that has been swapped out. We do not want to take 383// action on requests from a non-active renderer. The main exception is 384// for synchronous messages, which cannot be ignored without leaving the 385// renderer in a stuck state. See http://crbug.com/93427. 386TEST_F(RenderFrameHostManagerTest, FilterMessagesWhileSwappedOut) { 387 const GURL kChromeURL("chrome://foo"); 388 const GURL kDestUrl("http://www.google.com/"); 389 std::vector<FaviconURL> icons; 390 391 // Navigate our first tab to a chrome url and then to the destination. 392 NavigateActiveAndCommit(kChromeURL); 393 TestRenderViewHost* ntp_rvh = static_cast<TestRenderViewHost*>( 394 contents()->GetRenderManagerForTesting()->current_host()); 395 396 // Send an update favicon message and make sure it works. 397 const base::string16 ntp_title = base::ASCIIToUTF16("NTP Title"); 398 { 399 PluginFaviconMessageObserver observer(contents()); 400 EXPECT_TRUE(ntp_rvh->OnMessageReceived( 401 ViewHostMsg_UpdateFaviconURL( 402 rvh()->GetRoutingID(), 0, icons))); 403 EXPECT_TRUE(observer.favicon_received()); 404 } 405 // Create one more view in the same SiteInstance where ntp_rvh 406 // exists so that it doesn't get deleted on navigation to another 407 // site. 408 static_cast<SiteInstanceImpl*>(ntp_rvh->GetSiteInstance())-> 409 increment_active_view_count(); 410 411 412 // Navigate to a cross-site URL. 413 NavigateActiveAndCommit(kDestUrl); 414 TestRenderViewHost* dest_rvh = static_cast<TestRenderViewHost*>( 415 contents()->GetRenderViewHost()); 416 ASSERT_TRUE(dest_rvh); 417 EXPECT_NE(ntp_rvh, dest_rvh); 418 419 // The new RVH should be able to update its favicon. 420 const base::string16 dest_title = base::ASCIIToUTF16("Google"); 421 { 422 PluginFaviconMessageObserver observer(contents()); 423 EXPECT_TRUE( 424 dest_rvh->OnMessageReceived( 425 ViewHostMsg_UpdateFaviconURL(rvh()->GetRoutingID(), 101, icons))); 426 EXPECT_TRUE(observer.favicon_received()); 427 } 428 429 // The old renderer, being slow, now updates the favicon. It should be 430 // filtered out and not take effect. 431 EXPECT_TRUE(ntp_rvh->IsSwappedOut()); 432 { 433 PluginFaviconMessageObserver observer(contents()); 434 EXPECT_TRUE( 435 ntp_rvh->OnMessageReceived( 436 ViewHostMsg_UpdateFaviconURL(rvh()->GetRoutingID(), 101, icons))); 437 EXPECT_FALSE(observer.favicon_received()); 438 } 439 440 // The same logic should apply to RenderFrameHosts as well and routing through 441 // swapped out RFH shouldn't be allowed. Use a PluginCrashObserver to check 442 // if the IPC message is allowed through or not. 443 { 444 PluginFaviconMessageObserver observer(contents()); 445 // TODO(nasko): Check that the RFH is in swapped out when the state moves 446 // from RVH to RFH. 447 EXPECT_TRUE(ntp_rvh->main_render_frame_host()->OnMessageReceived( 448 FrameHostMsg_PluginCrashed( 449 main_rfh()->GetRoutingID(), base::FilePath(), 0))); 450 EXPECT_FALSE(observer.plugin_crashed()); 451 } 452 453 // We cannot filter out synchronous IPC messages, because the renderer would 454 // be left waiting for a reply. We pick RunBeforeUnloadConfirm as an example 455 // that can run easily within a unit test, and that needs to receive a reply 456 // without showing an actual dialog. 457 MockRenderProcessHost* ntp_process_host = 458 static_cast<MockRenderProcessHost*>(ntp_rvh->GetProcess()); 459 ntp_process_host->sink().ClearMessages(); 460 RenderFrameHost* ntp_rfh = ntp_rvh->GetMainFrame(); 461 const base::string16 msg = base::ASCIIToUTF16("Message"); 462 bool result = false; 463 base::string16 unused; 464 FrameHostMsg_RunBeforeUnloadConfirm before_unload_msg( 465 ntp_rfh->GetRoutingID(), kChromeURL, msg, false, &result, &unused); 466 // Enable pumping for check in BrowserMessageFilter::CheckCanDispatchOnUI. 467 before_unload_msg.EnableMessagePumping(); 468 EXPECT_TRUE(ntp_rfh->OnMessageReceived(before_unload_msg)); 469 EXPECT_TRUE(ntp_process_host->sink().GetUniqueMessageMatching(IPC_REPLY_ID)); 470 471 // Also test RunJavaScriptMessage. 472 ntp_process_host->sink().ClearMessages(); 473 FrameHostMsg_RunJavaScriptMessage js_msg( 474 ntp_rfh->GetRoutingID(), msg, msg, kChromeURL, 475 JAVASCRIPT_MESSAGE_TYPE_CONFIRM, &result, &unused); 476 js_msg.EnableMessagePumping(); 477 EXPECT_TRUE(ntp_rfh->OnMessageReceived(js_msg)); 478 EXPECT_TRUE(ntp_process_host->sink().GetUniqueMessageMatching(IPC_REPLY_ID)); 479} 480 481TEST_F(RenderFrameHostManagerTest, WhiteListSwapCompositorFrame) { 482 TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost(); 483 TestRenderWidgetHostView* swapped_out_rwhv = 484 static_cast<TestRenderWidgetHostView*>(swapped_out_rvh->GetView()); 485 EXPECT_FALSE(swapped_out_rwhv->did_swap_compositor_frame()); 486 487 MockRenderProcessHost* process_host = 488 static_cast<MockRenderProcessHost*>(swapped_out_rvh->GetProcess()); 489 process_host->sink().ClearMessages(); 490 491 cc::CompositorFrame frame; 492 ViewHostMsg_SwapCompositorFrame msg(rvh()->GetRoutingID(), 0, frame); 493 494 EXPECT_TRUE(swapped_out_rvh->OnMessageReceived(msg)); 495 EXPECT_TRUE(swapped_out_rwhv->did_swap_compositor_frame()); 496} 497 498TEST_F(RenderFrameHostManagerTest, WhiteListDidActivateAcceleratedCompositing) { 499 TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost(); 500 501 MockRenderProcessHost* process_host = 502 static_cast<MockRenderProcessHost*>(swapped_out_rvh->GetProcess()); 503 process_host->sink().ClearMessages(); 504 ViewHostMsg_DidActivateAcceleratedCompositing msg( 505 rvh()->GetRoutingID(), true); 506 EXPECT_TRUE(swapped_out_rvh->OnMessageReceived(msg)); 507 EXPECT_TRUE(swapped_out_rvh->is_accelerated_compositing_active()); 508} 509 510// Test if RenderViewHost::GetRenderWidgetHosts() only returns active 511// widgets. 512TEST_F(RenderFrameHostManagerTest, GetRenderWidgetHostsReturnsActiveViews) { 513 TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost(); 514 EXPECT_TRUE(swapped_out_rvh->IsSwappedOut()); 515 516 scoped_ptr<RenderWidgetHostIterator> widgets( 517 RenderWidgetHost::GetRenderWidgetHosts()); 518 // We know that there is the only one active widget. Another view is 519 // now swapped out, so the swapped out view is not included in the 520 // list. 521 RenderWidgetHost* widget = widgets->GetNextHost(); 522 EXPECT_FALSE(widgets->GetNextHost()); 523 RenderViewHost* rvh = RenderViewHost::From(widget); 524 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, 525 static_cast<RenderViewHostImpl*>(rvh)->rvh_state()); 526} 527 528// Test if RenderViewHost::GetRenderWidgetHosts() returns a subset of 529// RenderViewHostImpl::GetAllRenderWidgetHosts(). 530// RenderViewHost::GetRenderWidgetHosts() returns only active widgets, but 531// RenderViewHostImpl::GetAllRenderWidgetHosts() returns everything 532// including swapped out ones. 533TEST_F(RenderFrameHostManagerTest, 534 GetRenderWidgetHostsWithinGetAllRenderWidgetHosts) { 535 TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost(); 536 EXPECT_TRUE(swapped_out_rvh->IsSwappedOut()); 537 538 scoped_ptr<RenderWidgetHostIterator> widgets( 539 RenderWidgetHost::GetRenderWidgetHosts()); 540 541 while (RenderWidgetHost* w = widgets->GetNextHost()) { 542 bool found = false; 543 scoped_ptr<RenderWidgetHostIterator> all_widgets( 544 RenderWidgetHostImpl::GetAllRenderWidgetHosts()); 545 while (RenderWidgetHost* widget = all_widgets->GetNextHost()) { 546 if (w == widget) { 547 found = true; 548 break; 549 } 550 } 551 EXPECT_TRUE(found); 552 } 553} 554 555// Test if SiteInstanceImpl::active_view_count() is correctly updated 556// as views in a SiteInstance get swapped out and in. 557TEST_F(RenderFrameHostManagerTest, ActiveViewCountWhileSwappingInandOut) { 558 const GURL kUrl1("http://www.google.com/"); 559 const GURL kUrl2("http://www.chromium.org/"); 560 561 // Navigate to an initial URL. 562 contents()->NavigateAndCommit(kUrl1); 563 TestRenderViewHost* rvh1 = test_rvh(); 564 565 SiteInstanceImpl* instance1 = 566 static_cast<SiteInstanceImpl*>(rvh1->GetSiteInstance()); 567 EXPECT_EQ(instance1->active_view_count(), 1U); 568 569 // Create 2 new tabs and simulate them being the opener chain for the main 570 // tab. They should be in the same SiteInstance. 571 scoped_ptr<TestWebContents> opener1( 572 TestWebContents::Create(browser_context(), instance1)); 573 contents()->SetOpener(opener1.get()); 574 575 scoped_ptr<TestWebContents> opener2( 576 TestWebContents::Create(browser_context(), instance1)); 577 opener1->SetOpener(opener2.get()); 578 579 EXPECT_EQ(instance1->active_view_count(), 3U); 580 581 // Navigate to a cross-site URL (different SiteInstance but same 582 // BrowsingInstance). 583 contents()->NavigateAndCommit(kUrl2); 584 TestRenderViewHost* rvh2 = test_rvh(); 585 SiteInstanceImpl* instance2 = 586 static_cast<SiteInstanceImpl*>(rvh2->GetSiteInstance()); 587 588 // rvh2 is on chromium.org which is different from google.com on 589 // which other tabs are. 590 EXPECT_EQ(instance2->active_view_count(), 1U); 591 592 // There are two active views on google.com now. 593 EXPECT_EQ(instance1->active_view_count(), 2U); 594 595 // Navigate to the original origin (google.com). 596 contents()->NavigateAndCommit(kUrl1); 597 598 EXPECT_EQ(instance1->active_view_count(), 3U); 599} 600 601// This deletes a WebContents when the given RVH is deleted. This is 602// only for testing whether deleting an RVH does not cause any UaF in 603// other parts of the system. For now, this class is only used for the 604// next test cases to detect the bug mentioned at 605// http://crbug.com/259859. 606class RenderViewHostDestroyer : public WebContentsObserver { 607 public: 608 RenderViewHostDestroyer(RenderViewHost* render_view_host, 609 WebContents* web_contents) 610 : WebContentsObserver(WebContents::FromRenderViewHost(render_view_host)), 611 render_view_host_(render_view_host), 612 web_contents_(web_contents) {} 613 614 virtual void RenderViewDeleted( 615 RenderViewHost* render_view_host) OVERRIDE { 616 if (render_view_host == render_view_host_) 617 delete web_contents_; 618 } 619 620 private: 621 RenderViewHost* render_view_host_; 622 WebContents* web_contents_; 623 624 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestroyer); 625}; 626 627// Test if ShutdownRenderViewHostsInSiteInstance() does not touch any 628// RenderWidget that has been freed while deleting a RenderViewHost in 629// a previous iteration. This is a regression test for 630// http://crbug.com/259859. 631TEST_F(RenderFrameHostManagerTest, 632 DetectUseAfterFreeInShutdownRenderViewHostsInSiteInstance) { 633 const GURL kChromeURL("chrome://newtab"); 634 const GURL kUrl1("http://www.google.com"); 635 const GURL kUrl2("http://www.chromium.org"); 636 637 // Navigate our first tab to a chrome url and then to the destination. 638 NavigateActiveAndCommit(kChromeURL); 639 TestRenderViewHost* ntp_rvh = static_cast<TestRenderViewHost*>( 640 contents()->GetRenderManagerForTesting()->current_host()); 641 642 // Create one more tab and navigate to kUrl1. web_contents is not 643 // wrapped as scoped_ptr since it intentionally deleted by destroyer 644 // below as part of this test. 645 TestWebContents* web_contents = 646 TestWebContents::Create(browser_context(), ntp_rvh->GetSiteInstance()); 647 web_contents->NavigateAndCommit(kUrl1); 648 RenderViewHostDestroyer destroyer(ntp_rvh, web_contents); 649 650 // This causes the first tab to navigate to kUrl2, which destroys 651 // the ntp_rvh in ShutdownRenderViewHostsInSiteInstance(). When 652 // ntp_rvh is destroyed, it also destroys the RVHs in web_contents 653 // too. This can test whether 654 // SiteInstanceImpl::ShutdownRenderViewHostsInSiteInstance() can 655 // touch any object freed in this way or not while iterating through 656 // all widgets. 657 contents()->NavigateAndCommit(kUrl2); 658} 659 660// When there is an error with the specified page, renderer exits view-source 661// mode. See WebFrameImpl::DidFail(). We check by this test that 662// EnableViewSourceMode message is sent on every navigation regardless 663// RenderView is being newly created or reused. 664TEST_F(RenderFrameHostManagerTest, AlwaysSendEnableViewSourceMode) { 665 const GURL kChromeUrl("chrome://foo"); 666 const GURL kUrl("view-source:http://foo"); 667 668 // We have to navigate to some page at first since without this, the first 669 // navigation will reuse the SiteInstance created by Init(), and the second 670 // one will create a new SiteInstance. Because current_instance and 671 // new_instance will be different, a new RenderViewHost will be created for 672 // the second navigation. We have to avoid this in order to exercise the 673 // target code patch. 674 NavigateActiveAndCommit(kChromeUrl); 675 676 // Navigate. 677 controller().LoadURL( 678 kUrl, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 679 // Simulate response from RenderFrame for DispatchBeforeUnload. 680 base::TimeTicks now = base::TimeTicks::Now(); 681 main_test_rfh()->OnMessageReceived(FrameHostMsg_BeforeUnload_ACK( 682 main_test_rfh()->GetRoutingID(), true, now, now)); 683 ASSERT_TRUE(pending_rvh()); // New pending RenderViewHost will be created. 684 RenderViewHost* last_rvh = pending_rvh(); 685 int32 new_id = contents()->GetMaxPageIDForSiteInstance( 686 active_rvh()->GetSiteInstance()) + 1; 687 pending_test_rvh()->SendNavigate(new_id, kUrl); 688 EXPECT_EQ(controller().GetLastCommittedEntryIndex(), 1); 689 ASSERT_TRUE(controller().GetLastCommittedEntry()); 690 EXPECT_TRUE(kUrl == controller().GetLastCommittedEntry()->GetURL()); 691 EXPECT_FALSE(controller().GetPendingEntry()); 692 // Because we're using TestWebContents and TestRenderViewHost in this 693 // unittest, no one calls WebContentsImpl::RenderViewCreated(). So, we see no 694 // EnableViewSourceMode message, here. 695 696 // Clear queued messages before load. 697 process()->sink().ClearMessages(); 698 // Navigate, again. 699 controller().LoadURL( 700 kUrl, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 701 // The same RenderViewHost should be reused. 702 EXPECT_FALSE(pending_rvh()); 703 EXPECT_TRUE(last_rvh == rvh()); 704 test_rvh()->SendNavigate(new_id, kUrl); // The same page_id returned. 705 EXPECT_EQ(controller().GetLastCommittedEntryIndex(), 1); 706 EXPECT_FALSE(controller().GetPendingEntry()); 707 // New message should be sent out to make sure to enter view-source mode. 708 EXPECT_TRUE(process()->sink().GetUniqueMessageMatching( 709 ViewMsg_EnableViewSourceMode::ID)); 710} 711 712// Tests the Init function by checking the initial RenderViewHost. 713TEST_F(RenderFrameHostManagerTest, Init) { 714 // Using TestBrowserContext. 715 SiteInstanceImpl* instance = 716 static_cast<SiteInstanceImpl*>(SiteInstance::Create(browser_context())); 717 EXPECT_FALSE(instance->HasSite()); 718 719 scoped_ptr<TestWebContents> web_contents( 720 TestWebContents::Create(browser_context(), instance)); 721 FrameTree tree(web_contents->GetFrameTree()->root()->navigator(), 722 web_contents.get(), web_contents.get(), 723 web_contents.get(), web_contents.get()); 724 RenderFrameHostManager* manager = tree.root()->render_manager(); 725 726 manager->Init(browser_context(), instance, MSG_ROUTING_NONE, 727 MSG_ROUTING_NONE); 728 729 RenderViewHostImpl* rvh = manager->current_host(); 730 RenderFrameHostImpl* rfh = manager->current_frame_host(); 731 ASSERT_TRUE(rvh); 732 ASSERT_TRUE(rfh); 733 EXPECT_EQ(rvh, rfh->render_view_host()); 734 EXPECT_EQ(instance, rvh->GetSiteInstance()); 735 EXPECT_EQ(web_contents.get(), rvh->GetDelegate()); 736 EXPECT_EQ(web_contents.get(), rfh->delegate()); 737 EXPECT_TRUE(manager->GetRenderWidgetHostView()); 738 EXPECT_FALSE(manager->pending_render_view_host()); 739} 740 741// Tests the Navigate function. We navigate three sites consecutively and check 742// how the pending/committed RenderViewHost are modified. 743TEST_F(RenderFrameHostManagerTest, Navigate) { 744 TestNotificationTracker notifications; 745 746 SiteInstance* instance = SiteInstance::Create(browser_context()); 747 748 scoped_ptr<TestWebContents> web_contents( 749 TestWebContents::Create(browser_context(), instance)); 750 notifications.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED, 751 Source<WebContents>(web_contents.get())); 752 753 // Create. 754 FrameTree tree(web_contents->GetFrameTree()->root()->navigator(), 755 web_contents.get(), web_contents.get(), 756 web_contents.get(), web_contents.get()); 757 RenderFrameHostManager* manager = tree.root()->render_manager(); 758 759 manager->Init(browser_context(), instance, MSG_ROUTING_NONE, 760 MSG_ROUTING_NONE); 761 762 RenderFrameHostImpl* host; 763 764 // 1) The first navigation. -------------------------- 765 const GURL kUrl1("http://www.google.com/"); 766 NavigationEntryImpl entry1( 767 NULL /* instance */, -1 /* page_id */, kUrl1, Referrer(), 768 base::string16() /* title */, PAGE_TRANSITION_TYPED, 769 false /* is_renderer_init */); 770 host = manager->Navigate(entry1); 771 772 // The RenderFrameHost created in Init will be reused. 773 EXPECT_TRUE(host == manager->current_frame_host()); 774 EXPECT_FALSE(manager->pending_frame_host()); 775 776 // Commit. 777 manager->DidNavigateFrame(host); 778 // Commit to SiteInstance should be delayed until RenderView commit. 779 EXPECT_TRUE(host == manager->current_frame_host()); 780 ASSERT_TRUE(host); 781 EXPECT_FALSE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())-> 782 HasSite()); 783 static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->SetSite(kUrl1); 784 785 // 2) Navigate to next site. ------------------------- 786 const GURL kUrl2("http://www.google.com/foo"); 787 NavigationEntryImpl entry2( 788 NULL /* instance */, -1 /* page_id */, kUrl2, 789 Referrer(kUrl1, blink::WebReferrerPolicyDefault), 790 base::string16() /* title */, PAGE_TRANSITION_LINK, 791 true /* is_renderer_init */); 792 host = manager->Navigate(entry2); 793 794 // The RenderFrameHost created in Init will be reused. 795 EXPECT_TRUE(host == manager->current_frame_host()); 796 EXPECT_FALSE(manager->pending_frame_host()); 797 798 // Commit. 799 manager->DidNavigateFrame(host); 800 EXPECT_TRUE(host == manager->current_frame_host()); 801 ASSERT_TRUE(host); 802 EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())-> 803 HasSite()); 804 805 // 3) Cross-site navigate to next site. -------------- 806 const GURL kUrl3("http://webkit.org/"); 807 NavigationEntryImpl entry3( 808 NULL /* instance */, -1 /* page_id */, kUrl3, 809 Referrer(kUrl2, blink::WebReferrerPolicyDefault), 810 base::string16() /* title */, PAGE_TRANSITION_LINK, 811 false /* is_renderer_init */); 812 host = manager->Navigate(entry3); 813 814 // A new RenderFrameHost should be created. 815 EXPECT_TRUE(manager->pending_frame_host()); 816 ASSERT_EQ(host, manager->pending_frame_host()); 817 818 notifications.Reset(); 819 820 // Commit. 821 manager->DidNavigateFrame(manager->pending_frame_host()); 822 EXPECT_TRUE(host == manager->current_frame_host()); 823 ASSERT_TRUE(host); 824 EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())-> 825 HasSite()); 826 // Check the pending RenderFrameHost has been committed. 827 EXPECT_FALSE(manager->pending_frame_host()); 828 829 // We should observe a notification. 830 EXPECT_TRUE( 831 notifications.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED)); 832} 833 834// Tests the Navigate function. In this unit test we verify that the Navigate 835// function can handle a new navigation event before the previous navigation 836// has been committed. This is also a regression test for 837// http://crbug.com/104600. 838TEST_F(RenderFrameHostManagerTest, NavigateWithEarlyReNavigation) { 839 TestNotificationTracker notifications; 840 841 SiteInstance* instance = SiteInstance::Create(browser_context()); 842 843 scoped_ptr<TestWebContents> web_contents( 844 TestWebContents::Create(browser_context(), instance)); 845 notifications.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED, 846 Source<WebContents>(web_contents.get())); 847 848 // Create. 849 FrameTree tree(web_contents->GetFrameTree()->root()->navigator(), 850 web_contents.get(), web_contents.get(), 851 web_contents.get(), web_contents.get()); 852 RenderFrameHostManager* manager = tree.root()->render_manager(); 853 854 manager->Init(browser_context(), instance, MSG_ROUTING_NONE, 855 MSG_ROUTING_NONE); 856 857 // 1) The first navigation. -------------------------- 858 const GURL kUrl1("http://www.google.com/"); 859 NavigationEntryImpl entry1(NULL /* instance */, -1 /* page_id */, kUrl1, 860 Referrer(), base::string16() /* title */, 861 PAGE_TRANSITION_TYPED, 862 false /* is_renderer_init */); 863 RenderFrameHostImpl* host = manager->Navigate(entry1); 864 865 // The RenderFrameHost created in Init will be reused. 866 EXPECT_TRUE(host == manager->current_frame_host()); 867 EXPECT_FALSE(manager->pending_frame_host()); 868 869 // We should observe a notification. 870 EXPECT_TRUE( 871 notifications.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED)); 872 notifications.Reset(); 873 874 // Commit. 875 manager->DidNavigateFrame(host); 876 877 // Commit to SiteInstance should be delayed until RenderView commit. 878 EXPECT_TRUE(host == manager->current_frame_host()); 879 ASSERT_TRUE(host); 880 EXPECT_FALSE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())-> 881 HasSite()); 882 static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->SetSite(kUrl1); 883 884 // 2) Cross-site navigate to next site. ------------------------- 885 const GURL kUrl2("http://www.example.com"); 886 NavigationEntryImpl entry2( 887 NULL /* instance */, -1 /* page_id */, kUrl2, Referrer(), 888 base::string16() /* title */, PAGE_TRANSITION_TYPED, 889 false /* is_renderer_init */); 890 RenderFrameHostImpl* host2 = manager->Navigate(entry2); 891 int host2_process_id = host2->GetProcess()->GetID(); 892 893 // A new RenderFrameHost should be created. 894 EXPECT_TRUE(manager->pending_frame_host()); 895 ASSERT_EQ(host2, manager->pending_frame_host()); 896 EXPECT_NE(host2, host); 897 898 // Check that the navigation is still suspended because the old RVH 899 // is not swapped out, yet. 900 EXPECT_TRUE(host2->render_view_host()->are_navigations_suspended()); 901 MockRenderProcessHost* test_process_host2 = 902 static_cast<MockRenderProcessHost*>(host2->GetProcess()); 903 test_process_host2->sink().ClearMessages(); 904 host2->render_view_host()->NavigateToURL(kUrl2); 905 EXPECT_FALSE(test_process_host2->sink().GetUniqueMessageMatching( 906 FrameMsg_Navigate::ID)); 907 908 // Allow closing the current Render View (precondition for swapping out 909 // the RVH): Simulate response from RenderFrame for FrameMsg_BeforeUnload sent 910 // by DispatchBeforeUnload. 911 TestRenderViewHost* test_host = 912 static_cast<TestRenderViewHost*>(host->render_view_host()); 913 MockRenderProcessHost* test_process_host = 914 static_cast<MockRenderProcessHost*>(test_host->GetProcess()); 915 EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching( 916 FrameMsg_BeforeUnload::ID)); 917 test_host->SendBeforeUnloadACK(true); 918 919 // CrossSiteResourceHandler::StartCrossSiteTransition triggers a 920 // call of RenderFrameHostManager::SwapOutOldPage before 921 // RenderFrameHostManager::DidNavigateFrame is called. 922 // The RVH is swapped out after receiving the unload ack. 923 manager->SwapOutOldPage(); 924 EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching( 925 FrameMsg_SwapOut::ID)); 926 test_host->OnSwappedOut(false); 927 928 EXPECT_EQ(host, manager->current_frame_host()); 929 EXPECT_FALSE(manager->current_frame_host()->is_swapped_out()); 930 EXPECT_EQ(host2, manager->pending_frame_host()); 931 // There should be still no navigation messages being sent. 932 EXPECT_FALSE(test_process_host2->sink().GetUniqueMessageMatching( 933 FrameMsg_Navigate::ID)); 934 935 // 3) Cross-site navigate to next site before 2) has committed. -------------- 936 const GURL kUrl3("http://webkit.org/"); 937 NavigationEntryImpl entry3(NULL /* instance */, -1 /* page_id */, kUrl3, 938 Referrer(), base::string16() /* title */, 939 PAGE_TRANSITION_TYPED, 940 false /* is_renderer_init */); 941 test_process_host->sink().ClearMessages(); 942 RenderFrameHostImpl* host3 = manager->Navigate(entry3); 943 944 // A new RenderFrameHost should be created. host2 is now deleted. 945 EXPECT_TRUE(manager->pending_frame_host()); 946 ASSERT_EQ(host3, manager->pending_frame_host()); 947 EXPECT_NE(host3, host); 948 EXPECT_NE(host3->GetProcess()->GetID(), host2_process_id); 949 950 // Navigations in the new RVH should be suspended. 951 EXPECT_TRUE(static_cast<RenderViewHostImpl*>( 952 host3->render_view_host())->are_navigations_suspended()); 953 EXPECT_EQ(host, manager->current_frame_host()); 954 EXPECT_FALSE(manager->current_frame_host()->is_swapped_out()); 955 956 // Simulate a response to the second beforeunload request. 957 EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching( 958 FrameMsg_BeforeUnload::ID)); 959 test_host->SendBeforeUnloadACK(true); 960 961 // CrossSiteResourceHandler::StartCrossSiteTransition triggers a 962 // call of RenderFrameHostManager::SwapOutOldPage before 963 // RenderFrameHostManager::DidNavigateFrame is called. Since the previous 964 // navigation has already caused the renderer to start swapping out, there 965 // will be no more SwapOut messages being sent. 966 manager->SwapOutOldPage(); 967 EXPECT_FALSE(test_process_host->sink().GetUniqueMessageMatching( 968 FrameMsg_SwapOut::ID)); 969 test_host->OnSwappedOut(false); 970 971 // Commit. 972 manager->DidNavigateFrame(host3); 973 EXPECT_TRUE(host3 == manager->current_frame_host()); 974 ASSERT_TRUE(host3); 975 EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host3->GetSiteInstance())-> 976 HasSite()); 977 // Check the pending RenderFrameHost has been committed. 978 EXPECT_FALSE(manager->pending_frame_host()); 979 980 // We should observe a notification. 981 EXPECT_TRUE( 982 notifications.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED)); 983} 984 985// Test that navigation is not blocked when we make new navigation before 986// previous one has been committed. This is also a regression test for 987// http://crbug.com/104600. 988TEST_F(RenderFrameHostManagerTest, NewCrossNavigationBetweenSwapOutAndCommit) { 989 const GURL kUrl1("http://www.google.com/"); 990 const GURL kUrl2("http://www.chromium.org/"); 991 const GURL kUrl3("http://www.youtube.com/"); 992 993 contents()->NavigateAndCommit(kUrl1); 994 TestRenderViewHost* rvh1 = test_rvh(); 995 996 // Keep active_view_count nonzero so that no swapped out views in 997 // this SiteInstance get forcefully deleted. 998 static_cast<SiteInstanceImpl*>(rvh1->GetSiteInstance())-> 999 increment_active_view_count(); 1000 1001 // Navigate but don't commit. 1002 contents()->GetController().LoadURL( 1003 kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string()); 1004 EXPECT_TRUE(rvh1->is_waiting_for_beforeunload_ack()); 1005 contents()->ProceedWithCrossSiteNavigation(); 1006 EXPECT_FALSE(rvh1->is_waiting_for_beforeunload_ack()); 1007 StartCrossSiteTransition(contents()); 1008 EXPECT_TRUE(rvh1->IsWaitingForUnloadACK()); 1009 1010 rvh1->OnSwappedOut(false); 1011 EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT, rvh1->rvh_state()); 1012 1013 TestRenderViewHost* rvh2 = pending_test_rvh(); 1014 EXPECT_TRUE(rvh2); 1015 static_cast<SiteInstanceImpl*>(rvh2->GetSiteInstance())-> 1016 increment_active_view_count(); 1017 1018 contents()->GetController().LoadURL( 1019 kUrl3, Referrer(), PAGE_TRANSITION_LINK, std::string()); 1020 // Pending rvh2 is already deleted. 1021 contents()->ProceedWithCrossSiteNavigation(); 1022 1023 TestRenderViewHost* rvh3 = pending_test_rvh(); 1024 EXPECT_TRUE(rvh3); 1025 // Navigation should be already unblocked by rvh1. 1026 EXPECT_FALSE(rvh3->are_navigations_suspended()); 1027} 1028 1029// Tests WebUI creation. 1030TEST_F(RenderFrameHostManagerTest, WebUI) { 1031 set_should_create_webui(true); 1032 SiteInstance* instance = SiteInstance::Create(browser_context()); 1033 1034 scoped_ptr<TestWebContents> web_contents( 1035 TestWebContents::Create(browser_context(), instance)); 1036 FrameTree tree(web_contents->GetFrameTree()->root()->navigator(), 1037 web_contents.get(), web_contents.get(), 1038 web_contents.get(), web_contents.get()); 1039 RenderFrameHostManager* manager = tree.root()->render_manager(); 1040 1041 manager->Init(browser_context(), instance, MSG_ROUTING_NONE, 1042 MSG_ROUTING_NONE); 1043 EXPECT_FALSE(manager->current_host()->IsRenderViewLive()); 1044 1045 const GURL kUrl("chrome://foo"); 1046 NavigationEntryImpl entry(NULL /* instance */, -1 /* page_id */, kUrl, 1047 Referrer(), base::string16() /* title */, 1048 PAGE_TRANSITION_TYPED, 1049 false /* is_renderer_init */); 1050 RenderFrameHostImpl* host = manager->Navigate(entry); 1051 1052 // We commit the pending RenderFrameHost immediately because the previous 1053 // RenderFrameHost was not live. We test a case where it is live in 1054 // WebUIInNewTab. 1055 EXPECT_TRUE(host); 1056 EXPECT_EQ(host, manager->current_frame_host()); 1057 EXPECT_FALSE(manager->pending_frame_host()); 1058 1059 // It's important that the site instance get set on the Web UI page as soon 1060 // as the navigation starts, rather than lazily after it commits, so we don't 1061 // try to re-use the SiteInstance/process for non Web UI things that may 1062 // get loaded in between. 1063 EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())-> 1064 HasSite()); 1065 EXPECT_EQ(kUrl, host->GetSiteInstance()->GetSiteURL()); 1066 1067 // The Web UI is committed immediately because the RenderViewHost has not been 1068 // used yet. UpdateRendererStateForNavigate() took the short cut path. 1069 EXPECT_FALSE(manager->pending_web_ui()); 1070 EXPECT_TRUE(manager->web_ui()); 1071 1072 // Commit. 1073 manager->DidNavigateFrame(host); 1074 EXPECT_TRUE( 1075 host->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI); 1076} 1077 1078// Tests that we can open a WebUI link in a new tab from a WebUI page and still 1079// grant the correct bindings. http://crbug.com/189101. 1080TEST_F(RenderFrameHostManagerTest, WebUIInNewTab) { 1081 set_should_create_webui(true); 1082 SiteInstance* blank_instance = SiteInstance::Create(browser_context()); 1083 1084 // Create a blank tab. 1085 scoped_ptr<TestWebContents> web_contents1( 1086 TestWebContents::Create(browser_context(), blank_instance)); 1087 FrameTree tree1(web_contents1->GetFrameTree()->root()->navigator(), 1088 web_contents1.get(), web_contents1.get(), 1089 web_contents1.get(), web_contents1.get()); 1090 RenderFrameHostManager* manager1 = tree1.root()->render_manager(); 1091 manager1->Init( 1092 browser_context(), blank_instance, MSG_ROUTING_NONE, MSG_ROUTING_NONE); 1093 // Test the case that new RVH is considered live. 1094 manager1->current_host()->CreateRenderView(base::string16(), -1, -1); 1095 1096 // Navigate to a WebUI page. 1097 const GURL kUrl1("chrome://foo"); 1098 NavigationEntryImpl entry1(NULL /* instance */, -1 /* page_id */, kUrl1, 1099 Referrer(), base::string16() /* title */, 1100 PAGE_TRANSITION_TYPED, 1101 false /* is_renderer_init */); 1102 RenderFrameHostImpl* host1 = manager1->Navigate(entry1); 1103 1104 // We should have a pending navigation to the WebUI RenderViewHost. 1105 // It should already have bindings. 1106 EXPECT_EQ(host1, manager1->pending_frame_host()); 1107 EXPECT_NE(host1, manager1->current_frame_host()); 1108 EXPECT_TRUE( 1109 host1->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI); 1110 1111 // Commit and ensure we still have bindings. 1112 manager1->DidNavigateFrame(host1); 1113 SiteInstance* webui_instance = host1->GetSiteInstance(); 1114 EXPECT_EQ(host1, manager1->current_frame_host()); 1115 EXPECT_TRUE( 1116 host1->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI); 1117 1118 // Now simulate clicking a link that opens in a new tab. 1119 scoped_ptr<TestWebContents> web_contents2( 1120 TestWebContents::Create(browser_context(), webui_instance)); 1121 FrameTree tree2(web_contents2->GetFrameTree()->root()->navigator(), 1122 web_contents2.get(), web_contents2.get(), 1123 web_contents2.get(), web_contents2.get()); 1124 RenderFrameHostManager* manager2 = tree2.root()->render_manager(); 1125 manager2->Init( 1126 browser_context(), webui_instance, MSG_ROUTING_NONE, MSG_ROUTING_NONE); 1127 // Make sure the new RVH is considered live. This is usually done in 1128 // RenderWidgetHost::Init when opening a new tab from a link. 1129 manager2->current_host()->CreateRenderView(base::string16(), -1, -1); 1130 1131 const GURL kUrl2("chrome://foo/bar"); 1132 NavigationEntryImpl entry2(NULL /* instance */, -1 /* page_id */, kUrl2, 1133 Referrer(), base::string16() /* title */, 1134 PAGE_TRANSITION_LINK, 1135 true /* is_renderer_init */); 1136 RenderFrameHostImpl* host2 = manager2->Navigate(entry2); 1137 1138 // No cross-process transition happens because we are already in the right 1139 // SiteInstance. We should grant bindings immediately. 1140 EXPECT_EQ(host2, manager2->current_frame_host()); 1141 EXPECT_TRUE( 1142 host2->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI); 1143 1144 manager2->DidNavigateFrame(host2); 1145} 1146 1147// Tests that we don't end up in an inconsistent state if a page does a back and 1148// then reload. http://crbug.com/51680 1149TEST_F(RenderFrameHostManagerTest, PageDoesBackAndReload) { 1150 const GURL kUrl1("http://www.google.com/"); 1151 const GURL kUrl2("http://www.evil-site.com/"); 1152 1153 // Navigate to a safe site, then an evil site. 1154 // This will switch RenderViewHosts. We cannot assert that the first and 1155 // second RVHs are different, though, because the first one may be promptly 1156 // deleted. 1157 contents()->NavigateAndCommit(kUrl1); 1158 contents()->NavigateAndCommit(kUrl2); 1159 RenderViewHost* evil_rvh = contents()->GetRenderViewHost(); 1160 1161 // Now let's simulate the evil page calling history.back(). 1162 contents()->OnGoToEntryAtOffset(-1); 1163 // We should have a new pending RVH. 1164 // Note that in this case, the navigation has not committed, so evil_rvh will 1165 // not be deleted yet. 1166 EXPECT_NE(evil_rvh, contents()->GetRenderManagerForTesting()-> 1167 pending_render_view_host()); 1168 1169 // Before that RVH has committed, the evil page reloads itself. 1170 FrameHostMsg_DidCommitProvisionalLoad_Params params; 1171 params.page_id = 1; 1172 params.url = kUrl2; 1173 params.transition = PAGE_TRANSITION_CLIENT_REDIRECT; 1174 params.should_update_history = false; 1175 params.gesture = NavigationGestureAuto; 1176 params.was_within_same_page = false; 1177 params.is_post = false; 1178 params.page_state = PageState::CreateFromURL(kUrl2); 1179 1180 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(evil_rvh); 1181 RenderFrameHostImpl* rfh = RenderFrameHostImpl::FromID( 1182 rvh->GetProcess()->GetID(), rvh->main_frame_routing_id()); 1183 contents()->GetFrameTree()->root()->navigator()->DidNavigate(rfh, params); 1184 1185 // That should have cancelled the pending RVH, and the evil RVH should be the 1186 // current one. 1187 EXPECT_TRUE(contents()->GetRenderManagerForTesting()-> 1188 pending_render_view_host() == NULL); 1189 EXPECT_EQ(evil_rvh, contents()->GetRenderManagerForTesting()->current_host()); 1190 1191 // Also we should not have a pending navigation entry. 1192 EXPECT_TRUE(contents()->GetController().GetPendingEntry() == NULL); 1193 NavigationEntry* entry = contents()->GetController().GetVisibleEntry(); 1194 ASSERT_TRUE(entry != NULL); 1195 EXPECT_EQ(kUrl2, entry->GetURL()); 1196} 1197 1198// Ensure that we can go back and forward even if a SwapOut ACK isn't received. 1199// See http://crbug.com/93427. 1200TEST_F(RenderFrameHostManagerTest, NavigateAfterMissingSwapOutACK) { 1201 const GURL kUrl1("http://www.google.com/"); 1202 const GURL kUrl2("http://www.chromium.org/"); 1203 1204 // Navigate to two pages. 1205 contents()->NavigateAndCommit(kUrl1); 1206 TestRenderViewHost* rvh1 = test_rvh(); 1207 1208 // Keep active_view_count nonzero so that no swapped out views in 1209 // this SiteInstance get forcefully deleted. 1210 static_cast<SiteInstanceImpl*>(rvh1->GetSiteInstance())-> 1211 increment_active_view_count(); 1212 1213 contents()->NavigateAndCommit(kUrl2); 1214 TestRenderViewHost* rvh2 = test_rvh(); 1215 static_cast<SiteInstanceImpl*>(rvh2->GetSiteInstance())-> 1216 increment_active_view_count(); 1217 1218 // Now go back, but suppose the SwapOut_ACK isn't received. This shouldn't 1219 // happen, but we have seen it when going back quickly across many entries 1220 // (http://crbug.com/93427). 1221 contents()->GetController().GoBack(); 1222 EXPECT_TRUE(rvh2->is_waiting_for_beforeunload_ack()); 1223 contents()->ProceedWithCrossSiteNavigation(); 1224 EXPECT_FALSE(rvh2->is_waiting_for_beforeunload_ack()); 1225 StartCrossSiteTransition(contents()); 1226 EXPECT_TRUE(rvh2->IsWaitingForUnloadACK()); 1227 1228 // The back navigation commits. 1229 const NavigationEntry* entry1 = contents()->GetController().GetPendingEntry(); 1230 rvh1->SendNavigate(entry1->GetPageID(), entry1->GetURL()); 1231 EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT, rvh2->rvh_state()); 1232 1233 // We should be able to navigate forward. 1234 contents()->GetController().GoForward(); 1235 contents()->ProceedWithCrossSiteNavigation(); 1236 StartCrossSiteTransition(contents()); 1237 const NavigationEntry* entry2 = contents()->GetController().GetPendingEntry(); 1238 rvh2->SendNavigate(entry2->GetPageID(), entry2->GetURL()); 1239 EXPECT_EQ(rvh2, rvh()); 1240 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state()); 1241 EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT, rvh1->rvh_state()); 1242 rvh1->OnSwappedOut(false); 1243 EXPECT_TRUE(rvh1->IsSwappedOut()); 1244} 1245 1246// Test that we create swapped out RVHs for the opener chain when navigating an 1247// opened tab cross-process. This allows us to support certain cross-process 1248// JavaScript calls (http://crbug.com/99202). 1249TEST_F(RenderFrameHostManagerTest, CreateSwappedOutOpenerRVHs) { 1250 const GURL kUrl1("http://www.google.com/"); 1251 const GURL kUrl2("http://www.chromium.org/"); 1252 const GURL kChromeUrl("chrome://foo"); 1253 1254 // Navigate to an initial URL. 1255 contents()->NavigateAndCommit(kUrl1); 1256 RenderFrameHostManager* manager = contents()->GetRenderManagerForTesting(); 1257 TestRenderViewHost* rvh1 = test_rvh(); 1258 1259 // Create 2 new tabs and simulate them being the opener chain for the main 1260 // tab. They should be in the same SiteInstance. 1261 scoped_ptr<TestWebContents> opener1( 1262 TestWebContents::Create(browser_context(), rvh1->GetSiteInstance())); 1263 RenderFrameHostManager* opener1_manager = 1264 opener1->GetRenderManagerForTesting(); 1265 contents()->SetOpener(opener1.get()); 1266 1267 scoped_ptr<TestWebContents> opener2( 1268 TestWebContents::Create(browser_context(), rvh1->GetSiteInstance())); 1269 RenderFrameHostManager* opener2_manager = 1270 opener2->GetRenderManagerForTesting(); 1271 opener1->SetOpener(opener2.get()); 1272 1273 // Navigate to a cross-site URL (different SiteInstance but same 1274 // BrowsingInstance). 1275 contents()->NavigateAndCommit(kUrl2); 1276 TestRenderViewHost* rvh2 = test_rvh(); 1277 EXPECT_NE(rvh1->GetSiteInstance(), rvh2->GetSiteInstance()); 1278 EXPECT_TRUE(rvh1->GetSiteInstance()->IsRelatedSiteInstance( 1279 rvh2->GetSiteInstance())); 1280 1281 // Ensure rvh1 is placed on swapped out list of the current tab. 1282 EXPECT_TRUE(manager->IsRVHOnSwappedOutList(rvh1)); 1283 EXPECT_EQ(rvh1, 1284 manager->GetSwappedOutRenderViewHost(rvh1->GetSiteInstance())); 1285 1286 // Ensure a swapped out RVH is created in the first opener tab. 1287 TestRenderViewHost* opener1_rvh = static_cast<TestRenderViewHost*>( 1288 opener1_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance())); 1289 EXPECT_TRUE(opener1_manager->IsRVHOnSwappedOutList(opener1_rvh)); 1290 EXPECT_TRUE(opener1_rvh->IsSwappedOut()); 1291 1292 // Ensure a swapped out RVH is created in the second opener tab. 1293 TestRenderViewHost* opener2_rvh = static_cast<TestRenderViewHost*>( 1294 opener2_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance())); 1295 EXPECT_TRUE(opener2_manager->IsRVHOnSwappedOutList(opener2_rvh)); 1296 EXPECT_TRUE(opener2_rvh->IsSwappedOut()); 1297 1298 // Navigate to a cross-BrowsingInstance URL. 1299 contents()->NavigateAndCommit(kChromeUrl); 1300 TestRenderViewHost* rvh3 = test_rvh(); 1301 EXPECT_NE(rvh1->GetSiteInstance(), rvh3->GetSiteInstance()); 1302 EXPECT_FALSE(rvh1->GetSiteInstance()->IsRelatedSiteInstance( 1303 rvh3->GetSiteInstance())); 1304 1305 // No scripting is allowed across BrowsingInstances, so we should not create 1306 // swapped out RVHs for the opener chain in this case. 1307 EXPECT_FALSE(opener1_manager->GetSwappedOutRenderViewHost( 1308 rvh3->GetSiteInstance())); 1309 EXPECT_FALSE(opener2_manager->GetSwappedOutRenderViewHost( 1310 rvh3->GetSiteInstance())); 1311} 1312 1313// Test that we clean up swapped out RenderViewHosts when a process hosting 1314// those associated RenderViews crashes. http://crbug.com/258993 1315TEST_F(RenderFrameHostManagerTest, CleanUpSwappedOutRVHOnProcessCrash) { 1316 const GURL kUrl1("http://www.google.com/"); 1317 const GURL kUrl2("http://www.chromium.org/"); 1318 1319 // Navigate to an initial URL. 1320 contents()->NavigateAndCommit(kUrl1); 1321 TestRenderViewHost* rvh1 = test_rvh(); 1322 1323 // Create a new tab as an opener for the main tab. 1324 scoped_ptr<TestWebContents> opener1( 1325 TestWebContents::Create(browser_context(), rvh1->GetSiteInstance())); 1326 RenderFrameHostManager* opener1_manager = 1327 opener1->GetRenderManagerForTesting(); 1328 contents()->SetOpener(opener1.get()); 1329 1330 // Make sure the new opener RVH is considered live. 1331 opener1_manager->current_host()->CreateRenderView(base::string16(), -1, -1); 1332 1333 // Use a cross-process navigation in the opener to swap out the old RVH. 1334 EXPECT_FALSE(opener1_manager->GetSwappedOutRenderViewHost( 1335 rvh1->GetSiteInstance())); 1336 opener1->NavigateAndCommit(kUrl2); 1337 EXPECT_TRUE(opener1_manager->GetSwappedOutRenderViewHost( 1338 rvh1->GetSiteInstance())); 1339 1340 // Fake a process crash. 1341 RenderProcessHost::RendererClosedDetails details( 1342 rvh1->GetProcess()->GetHandle(), 1343 base::TERMINATION_STATUS_PROCESS_CRASHED, 1344 0); 1345 NotificationService::current()->Notify( 1346 NOTIFICATION_RENDERER_PROCESS_CLOSED, 1347 Source<RenderProcessHost>(rvh1->GetProcess()), 1348 Details<RenderProcessHost::RendererClosedDetails>(&details)); 1349 rvh1->set_render_view_created(false); 1350 1351 // Ensure that the swapped out RenderViewHost has been deleted. 1352 EXPECT_FALSE(opener1_manager->GetSwappedOutRenderViewHost( 1353 rvh1->GetSiteInstance())); 1354 1355 // Reload the initial tab. This should recreate the opener's swapped out RVH 1356 // in the original SiteInstance. 1357 contents()->GetController().Reload(true); 1358 EXPECT_EQ(opener1_manager->GetSwappedOutRenderViewHost( 1359 rvh1->GetSiteInstance())->GetRoutingID(), 1360 test_rvh()->opener_route_id()); 1361} 1362 1363// Test that RenderViewHosts created for WebUI navigations are properly 1364// granted WebUI bindings even if an unprivileged swapped out RenderViewHost 1365// is in the same process (http://crbug.com/79918). 1366TEST_F(RenderFrameHostManagerTest, EnableWebUIWithSwappedOutOpener) { 1367 set_should_create_webui(true); 1368 const GURL kSettingsUrl("chrome://chrome/settings"); 1369 const GURL kPluginUrl("chrome://plugins"); 1370 1371 // Navigate to an initial WebUI URL. 1372 contents()->NavigateAndCommit(kSettingsUrl); 1373 1374 // Ensure the RVH has WebUI bindings. 1375 TestRenderViewHost* rvh1 = test_rvh(); 1376 EXPECT_TRUE(rvh1->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI); 1377 1378 // Create a new tab and simulate it being the opener for the main 1379 // tab. It should be in the same SiteInstance. 1380 scoped_ptr<TestWebContents> opener1( 1381 TestWebContents::Create(browser_context(), rvh1->GetSiteInstance())); 1382 RenderFrameHostManager* opener1_manager = 1383 opener1->GetRenderManagerForTesting(); 1384 contents()->SetOpener(opener1.get()); 1385 1386 // Navigate to a different WebUI URL (different SiteInstance, same 1387 // BrowsingInstance). 1388 contents()->NavigateAndCommit(kPluginUrl); 1389 TestRenderViewHost* rvh2 = test_rvh(); 1390 EXPECT_NE(rvh1->GetSiteInstance(), rvh2->GetSiteInstance()); 1391 EXPECT_TRUE(rvh1->GetSiteInstance()->IsRelatedSiteInstance( 1392 rvh2->GetSiteInstance())); 1393 1394 // Ensure a swapped out RVH is created in the first opener tab. 1395 TestRenderViewHost* opener1_rvh = static_cast<TestRenderViewHost*>( 1396 opener1_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance())); 1397 EXPECT_TRUE(opener1_manager->IsRVHOnSwappedOutList(opener1_rvh)); 1398 EXPECT_TRUE(opener1_rvh->IsSwappedOut()); 1399 1400 // Ensure the new RVH has WebUI bindings. 1401 EXPECT_TRUE(rvh2->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI); 1402} 1403 1404// Test that we reuse the same guest SiteInstance if we navigate across sites. 1405TEST_F(RenderFrameHostManagerTest, NoSwapOnGuestNavigations) { 1406 TestNotificationTracker notifications; 1407 1408 GURL guest_url(std::string(kGuestScheme).append("://abc123")); 1409 SiteInstance* instance = 1410 SiteInstance::CreateForURL(browser_context(), guest_url); 1411 scoped_ptr<TestWebContents> web_contents( 1412 TestWebContents::Create(browser_context(), instance)); 1413 1414 // Create. 1415 FrameTree tree(web_contents->GetFrameTree()->root()->navigator(), 1416 web_contents.get(), web_contents.get(), 1417 web_contents.get(), web_contents.get()); 1418 RenderFrameHostManager* manager = tree.root()->render_manager(); 1419 1420 manager->Init(browser_context(), instance, MSG_ROUTING_NONE, 1421 MSG_ROUTING_NONE); 1422 1423 RenderFrameHostImpl* host; 1424 1425 // 1) The first navigation. -------------------------- 1426 const GURL kUrl1("http://www.google.com/"); 1427 NavigationEntryImpl entry1( 1428 NULL /* instance */, -1 /* page_id */, kUrl1, Referrer(), 1429 base::string16() /* title */, PAGE_TRANSITION_TYPED, 1430 false /* is_renderer_init */); 1431 host = manager->Navigate(entry1); 1432 1433 // The RenderFrameHost created in Init will be reused. 1434 EXPECT_TRUE(host == manager->current_frame_host()); 1435 EXPECT_FALSE(manager->pending_frame_host()); 1436 EXPECT_EQ(manager->current_frame_host()->GetSiteInstance(), instance); 1437 1438 // Commit. 1439 manager->DidNavigateFrame(host); 1440 // Commit to SiteInstance should be delayed until RenderView commit. 1441 EXPECT_EQ(host, manager->current_frame_host()); 1442 ASSERT_TRUE(host); 1443 EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())-> 1444 HasSite()); 1445 1446 // 2) Navigate to a different domain. ------------------------- 1447 // Guests stay in the same process on navigation. 1448 const GURL kUrl2("http://www.chromium.org"); 1449 NavigationEntryImpl entry2( 1450 NULL /* instance */, -1 /* page_id */, kUrl2, 1451 Referrer(kUrl1, blink::WebReferrerPolicyDefault), 1452 base::string16() /* title */, PAGE_TRANSITION_LINK, 1453 true /* is_renderer_init */); 1454 host = manager->Navigate(entry2); 1455 1456 // The RenderFrameHost created in Init will be reused. 1457 EXPECT_EQ(host, manager->current_frame_host()); 1458 EXPECT_FALSE(manager->pending_frame_host()); 1459 1460 // Commit. 1461 manager->DidNavigateFrame(host); 1462 EXPECT_EQ(host, manager->current_frame_host()); 1463 ASSERT_TRUE(host); 1464 EXPECT_EQ(static_cast<SiteInstanceImpl*>(host->GetSiteInstance()), 1465 instance); 1466} 1467 1468// Test that we cancel a pending RVH if we close the tab while it's pending. 1469// http://crbug.com/294697. 1470TEST_F(RenderFrameHostManagerTest, NavigateWithEarlyClose) { 1471 TestNotificationTracker notifications; 1472 1473 SiteInstance* instance = SiteInstance::Create(browser_context()); 1474 1475 BeforeUnloadFiredWebContentsDelegate delegate; 1476 scoped_ptr<TestWebContents> web_contents( 1477 TestWebContents::Create(browser_context(), instance)); 1478 web_contents->SetDelegate(&delegate); 1479 notifications.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED, 1480 Source<WebContents>(web_contents.get())); 1481 1482 // Create. 1483 FrameTree tree(web_contents->GetFrameTree()->root()->navigator(), 1484 web_contents.get(), web_contents.get(), 1485 web_contents.get(), web_contents.get()); 1486 RenderFrameHostManager* manager = tree.root()->render_manager(); 1487 1488 manager->Init(browser_context(), instance, MSG_ROUTING_NONE, 1489 MSG_ROUTING_NONE); 1490 1491 // 1) The first navigation. -------------------------- 1492 const GURL kUrl1("http://www.google.com/"); 1493 NavigationEntryImpl entry1(NULL /* instance */, -1 /* page_id */, kUrl1, 1494 Referrer(), base::string16() /* title */, 1495 PAGE_TRANSITION_TYPED, 1496 false /* is_renderer_init */); 1497 RenderFrameHostImpl* host = manager->Navigate(entry1); 1498 1499 // The RenderFrameHost created in Init will be reused. 1500 EXPECT_EQ(host, manager->current_frame_host()); 1501 EXPECT_FALSE(manager->pending_frame_host()); 1502 1503 // We should observe a notification. 1504 EXPECT_TRUE( 1505 notifications.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED)); 1506 notifications.Reset(); 1507 1508 // Commit. 1509 manager->DidNavigateFrame(host); 1510 1511 // Commit to SiteInstance should be delayed until RenderFrame commits. 1512 EXPECT_EQ(host, manager->current_frame_host()); 1513 EXPECT_FALSE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())-> 1514 HasSite()); 1515 static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->SetSite(kUrl1); 1516 1517 // 2) Cross-site navigate to next site. ------------------------- 1518 const GURL kUrl2("http://www.example.com"); 1519 NavigationEntryImpl entry2( 1520 NULL /* instance */, -1 /* page_id */, kUrl2, Referrer(), 1521 base::string16() /* title */, PAGE_TRANSITION_TYPED, 1522 false /* is_renderer_init */); 1523 RenderFrameHostImpl* host2 = manager->Navigate(entry2); 1524 1525 // A new RenderFrameHost should be created. 1526 ASSERT_EQ(host2, manager->pending_frame_host()); 1527 EXPECT_NE(host2, host); 1528 1529 EXPECT_EQ(host, manager->current_frame_host()); 1530 EXPECT_FALSE(manager->current_frame_host()->is_swapped_out()); 1531 EXPECT_EQ(host2, manager->pending_frame_host()); 1532 1533 // 3) Close the tab. ------------------------- 1534 notifications.ListenFor(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED, 1535 Source<RenderWidgetHost>(host2->render_view_host())); 1536 manager->OnBeforeUnloadACK(false, true, base::TimeTicks()); 1537 1538 EXPECT_TRUE( 1539 notifications.Check1AndReset(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED)); 1540 EXPECT_FALSE(manager->pending_frame_host()); 1541 EXPECT_EQ(host, manager->current_frame_host()); 1542} 1543 1544// Tests that the RenderViewHost is properly deleted when the SwapOutACK is 1545// received before the new page commits. 1546TEST_F(RenderFrameHostManagerTest, 1547 SwapOutACKBeforeNewPageCommitsLeadsToDeletion) { 1548 const GURL kUrl1("http://www.google.com/"); 1549 const GURL kUrl2("http://www.chromium.org/"); 1550 1551 // Navigate to the first page. 1552 contents()->NavigateAndCommit(kUrl1); 1553 TestRenderViewHost* rvh1 = test_rvh(); 1554 RenderViewHostDeletedObserver rvh_deleted_observer(rvh1); 1555 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state()); 1556 1557 // Navigate to new site, simulating onbeforeunload approval. 1558 controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string()); 1559 base::TimeTicks now = base::TimeTicks::Now(); 1560 main_test_rfh()->OnMessageReceived( 1561 FrameHostMsg_BeforeUnload_ACK(0, true, now, now)); 1562 EXPECT_TRUE(contents()->cross_navigation_pending()); 1563 TestRenderViewHost* rvh2 = 1564 static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost()); 1565 1566 // Simulate rvh2's response, which leads to an unload request being sent to 1567 // rvh1. 1568 std::vector<GURL> url_chain; 1569 url_chain.push_back(GURL()); 1570 contents()->GetRenderManagerForTesting()->OnCrossSiteResponse( 1571 contents()->GetRenderManagerForTesting()->pending_frame_host(), 1572 GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(), 1573 url_chain, Referrer(), PAGE_TRANSITION_TYPED, false); 1574 EXPECT_TRUE(contents()->cross_navigation_pending()); 1575 EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK, 1576 rvh1->rvh_state()); 1577 1578 // Simulate the swap out ack. 1579 rvh1->OnSwappedOut(false); 1580 EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT, rvh1->rvh_state()); 1581 1582 // The new page commits. 1583 contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED); 1584 EXPECT_FALSE(contents()->cross_navigation_pending()); 1585 EXPECT_EQ(rvh2, rvh()); 1586 EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); 1587 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state()); 1588 1589 // rvh1 should have been deleted. 1590 EXPECT_TRUE(rvh_deleted_observer.deleted()); 1591 rvh1 = NULL; 1592} 1593 1594// Tests that the RenderViewHost is properly swapped out when the SwapOutACK is 1595// received before the new page commits. 1596TEST_F(RenderFrameHostManagerTest, 1597 SwapOutACKBeforeNewPageCommitsLeadsToSwapOut) { 1598 const GURL kUrl1("http://www.google.com/"); 1599 const GURL kUrl2("http://www.chromium.org/"); 1600 1601 // Navigate to the first page. 1602 contents()->NavigateAndCommit(kUrl1); 1603 TestRenderViewHost* rvh1 = test_rvh(); 1604 RenderViewHostDeletedObserver rvh_deleted_observer(rvh1); 1605 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state()); 1606 1607 // Increment the number of active views in SiteInstanceImpl so that rvh2 is 1608 // not deleted on swap out. 1609 static_cast<SiteInstanceImpl*>( 1610 rvh1->GetSiteInstance())->increment_active_view_count(); 1611 1612 // Navigate to new site, simulating onbeforeunload approval. 1613 controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string()); 1614 base::TimeTicks now = base::TimeTicks::Now(); 1615 main_test_rfh()->OnMessageReceived( 1616 FrameHostMsg_BeforeUnload_ACK(0, true, now, now)); 1617 EXPECT_TRUE(contents()->cross_navigation_pending()); 1618 TestRenderViewHost* rvh2 = 1619 static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost()); 1620 1621 // Simulate rvh2's response, which leads to an unload request being sent to 1622 // rvh1. 1623 std::vector<GURL> url_chain; 1624 url_chain.push_back(GURL()); 1625 contents()->GetRenderManagerForTesting()->OnCrossSiteResponse( 1626 contents()->GetRenderManagerForTesting()->pending_frame_host(), 1627 GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(), 1628 url_chain, Referrer(), PAGE_TRANSITION_TYPED, false); 1629 EXPECT_TRUE(contents()->cross_navigation_pending()); 1630 EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK, 1631 rvh1->rvh_state()); 1632 1633 // Simulate the swap out ack. 1634 rvh1->OnSwappedOut(false); 1635 EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT, rvh1->rvh_state()); 1636 1637 // The new page commits. 1638 contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED); 1639 EXPECT_FALSE(contents()->cross_navigation_pending()); 1640 EXPECT_EQ(rvh2, rvh()); 1641 EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); 1642 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state()); 1643 1644 // rvh1 should be swapped out. 1645 EXPECT_FALSE(rvh_deleted_observer.deleted()); 1646 EXPECT_TRUE(rvh1->IsSwappedOut()); 1647} 1648 1649// Tests that the RenderViewHost is properly deleted when the new 1650// page commits before the swap out ack is received. 1651TEST_F(RenderFrameHostManagerTest, 1652 NewPageCommitsBeforeSwapOutACKLeadsToDeletion) { 1653 const GURL kUrl1("http://www.google.com/"); 1654 const GURL kUrl2("http://www.chromium.org/"); 1655 1656 // Navigate to the first page. 1657 contents()->NavigateAndCommit(kUrl1); 1658 TestRenderViewHost* rvh1 = test_rvh(); 1659 RenderViewHostDeletedObserver rvh_deleted_observer(rvh1); 1660 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state()); 1661 1662 // Navigate to new site, simulating onbeforeunload approval. 1663 controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string()); 1664 base::TimeTicks now = base::TimeTicks::Now(); 1665 main_test_rfh()->OnMessageReceived( 1666 FrameHostMsg_BeforeUnload_ACK(0, true, now, now)); 1667 EXPECT_TRUE(contents()->cross_navigation_pending()); 1668 TestRenderViewHost* rvh2 = 1669 static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost()); 1670 1671 // Simulate rvh2's response, which leads to an unload request being sent to 1672 // rvh1. 1673 std::vector<GURL> url_chain; 1674 url_chain.push_back(GURL()); 1675 contents()->GetRenderManagerForTesting()->OnCrossSiteResponse( 1676 contents()->GetRenderManagerForTesting()->pending_frame_host(), 1677 GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(), 1678 url_chain, Referrer(), PAGE_TRANSITION_TYPED, false); 1679 EXPECT_TRUE(contents()->cross_navigation_pending()); 1680 EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK, 1681 rvh1->rvh_state()); 1682 1683 // The new page commits. 1684 contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED); 1685 EXPECT_FALSE(contents()->cross_navigation_pending()); 1686 EXPECT_EQ(rvh2, rvh()); 1687 EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); 1688 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state()); 1689 EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SHUTDOWN, rvh1->rvh_state()); 1690 1691 // Simulate the swap out ack. 1692 rvh1->OnSwappedOut(false); 1693 1694 // rvh1 should have been deleted. 1695 EXPECT_TRUE(rvh_deleted_observer.deleted()); 1696 rvh1 = NULL; 1697} 1698 1699// Tests that the RenderViewHost is properly swapped out when the new page 1700// commits before the swap out ack is received. 1701TEST_F(RenderFrameHostManagerTest, 1702 NewPageCommitsBeforeSwapOutACKLeadsToSwapOut) { 1703 const GURL kUrl1("http://www.google.com/"); 1704 const GURL kUrl2("http://www.chromium.org/"); 1705 1706 // Navigate to the first page. 1707 contents()->NavigateAndCommit(kUrl1); 1708 TestRenderViewHost* rvh1 = test_rvh(); 1709 RenderViewHostDeletedObserver rvh_deleted_observer(rvh1); 1710 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state()); 1711 1712 // Increment the number of active views in SiteInstanceImpl so that rvh1 is 1713 // not deleted on swap out. 1714 static_cast<SiteInstanceImpl*>( 1715 rvh1->GetSiteInstance())->increment_active_view_count(); 1716 1717 // Navigate to new site, simulating onbeforeunload approval. 1718 controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string()); 1719 base::TimeTicks now = base::TimeTicks::Now(); 1720 main_test_rfh()->OnMessageReceived( 1721 FrameHostMsg_BeforeUnload_ACK(0, true, now, now)); 1722 EXPECT_TRUE(contents()->cross_navigation_pending()); 1723 TestRenderViewHost* rvh2 = 1724 static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost()); 1725 1726 // Simulate rvh2's response, which leads to an unload request being sent to 1727 // rvh1. 1728 std::vector<GURL> url_chain; 1729 url_chain.push_back(GURL()); 1730 contents()->GetRenderManagerForTesting()->OnCrossSiteResponse( 1731 contents()->GetRenderManagerForTesting()->pending_frame_host(), 1732 GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(), 1733 url_chain, Referrer(), PAGE_TRANSITION_TYPED, false); 1734 EXPECT_TRUE(contents()->cross_navigation_pending()); 1735 EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK, 1736 rvh1->rvh_state()); 1737 1738 // The new page commits. 1739 contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED); 1740 EXPECT_FALSE(contents()->cross_navigation_pending()); 1741 EXPECT_EQ(rvh2, rvh()); 1742 EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); 1743 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state()); 1744 EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT, rvh1->rvh_state()); 1745 1746 // Simulate the swap out ack. 1747 rvh1->OnSwappedOut(false); 1748 1749 // rvh1 should be swapped out. 1750 EXPECT_FALSE(rvh_deleted_observer.deleted()); 1751 EXPECT_TRUE(rvh1->IsSwappedOut()); 1752} 1753 1754// Test that the RenderViewHost is properly swapped out if a navigation in the 1755// new renderer commits before sending the SwapOut message to the old renderer. 1756// This simulates a cross-site navigation to a synchronously committing URL 1757// (e.g., a data URL) and ensures it works properly. 1758TEST_F(RenderFrameHostManagerTest, 1759 CommitNewNavigationBeforeSendingSwapOut) { 1760 const GURL kUrl1("http://www.google.com/"); 1761 const GURL kUrl2("http://www.chromium.org/"); 1762 1763 // Navigate to the first page. 1764 contents()->NavigateAndCommit(kUrl1); 1765 TestRenderViewHost* rvh1 = test_rvh(); 1766 RenderViewHostDeletedObserver rvh_deleted_observer(rvh1); 1767 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state()); 1768 1769 // Increment the number of active views in SiteInstanceImpl so that rvh1 is 1770 // not deleted on swap out. 1771 static_cast<SiteInstanceImpl*>( 1772 rvh1->GetSiteInstance())->increment_active_view_count(); 1773 1774 // Navigate to new site, simulating onbeforeunload approval. 1775 controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string()); 1776 base::TimeTicks now = base::TimeTicks::Now(); 1777 main_test_rfh()->OnMessageReceived( 1778 FrameHostMsg_BeforeUnload_ACK(0, true, now, now)); 1779 EXPECT_TRUE(contents()->cross_navigation_pending()); 1780 TestRenderViewHost* rvh2 = 1781 static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost()); 1782 1783 // The new page commits. 1784 contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED); 1785 EXPECT_FALSE(contents()->cross_navigation_pending()); 1786 EXPECT_EQ(rvh2, rvh()); 1787 EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); 1788 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state()); 1789 EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT, rvh1->rvh_state()); 1790 1791 // Simulate the swap out ack. 1792 rvh1->OnSwappedOut(false); 1793 1794 // rvh1 should be swapped out. 1795 EXPECT_FALSE(rvh_deleted_observer.deleted()); 1796 EXPECT_TRUE(rvh1->IsSwappedOut()); 1797} 1798 1799// Test that a RenderFrameHost is properly deleted or swapped out when a 1800// cross-site navigation is cancelled. 1801TEST_F(RenderFrameHostManagerTest, 1802 CancelPendingProperlyDeletesOrSwaps) { 1803 const GURL kUrl1("http://www.google.com/"); 1804 const GURL kUrl2("http://www.chromium.org/"); 1805 RenderFrameHostImpl* pending_rfh = NULL; 1806 base::TimeTicks now = base::TimeTicks::Now(); 1807 1808 // Navigate to the first page. 1809 contents()->NavigateAndCommit(kUrl1); 1810 TestRenderViewHost* rvh1 = test_rvh(); 1811 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state()); 1812 1813 // Navigate to a new site, starting a cross-site navigation. 1814 controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string()); 1815 { 1816 pending_rfh = contents()->GetFrameTree()->root()->render_manager() 1817 ->pending_frame_host(); 1818 RenderFrameHostDeletedObserver rvh_deleted_observer(pending_rfh); 1819 1820 // Cancel the navigation by simulating a declined beforeunload dialog. 1821 main_test_rfh()->OnMessageReceived( 1822 FrameHostMsg_BeforeUnload_ACK(0, false, now, now)); 1823 EXPECT_FALSE(contents()->cross_navigation_pending()); 1824 1825 // Since the pending RFH is the only one for the new SiteInstance, it should 1826 // be deleted. 1827 EXPECT_TRUE(rvh_deleted_observer.deleted()); 1828 } 1829 1830 // Start another cross-site navigation. 1831 controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string()); 1832 { 1833 pending_rfh = contents()->GetFrameTree()->root()->render_manager() 1834 ->pending_frame_host(); 1835 RenderFrameHostDeletedObserver rvh_deleted_observer(pending_rfh); 1836 1837 // Increment the number of active views in the new SiteInstance, which will 1838 // cause the pending RFH to be swapped out instead of deleted. 1839 static_cast<SiteInstanceImpl*>( 1840 pending_rfh->GetSiteInstance())->increment_active_view_count(); 1841 1842 main_test_rfh()->OnMessageReceived( 1843 FrameHostMsg_BeforeUnload_ACK(0, false, now, now)); 1844 EXPECT_FALSE(contents()->cross_navigation_pending()); 1845 EXPECT_FALSE(rvh_deleted_observer.deleted()); 1846 } 1847} 1848 1849} // namespace content 1850