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