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