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