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