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