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