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