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