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.
5 #include "base/strings/utf_string_conversions.h"
6 #include "content/browser/frame_host/navigation_controller_impl.h"
7 #include "content/browser/frame_host/navigation_entry_impl.h"
8 #include "content/browser/frame_host/render_frame_host_manager.h"
9 #include "content/browser/site_instance_impl.h"
10 #include "content/browser/webui/web_ui_controller_factory_registry.h"
11 #include "content/common/view_messages.h"
12 #include "content/public/browser/notification_details.h"
13 #include "content/public/browser/notification_service.h"
14 #include "content/public/browser/notification_source.h"
15 #include "content/public/browser/notification_types.h"
16 #include "content/public/browser/render_process_host.h"
17 #include "content/public/browser/render_widget_host_iterator.h"
18 #include "content/public/browser/web_contents_delegate.h"
19 #include "content/public/browser/web_contents_observer.h"
20 #include "content/public/browser/web_ui_controller.h"
21 #include "content/public/common/bindings_policy.h"
22 #include "content/public/common/javascript_message_type.h"
23 #include "content/public/common/page_transition_types.h"
24 #include "content/public/common/url_constants.h"
25 #include "content/public/common/url_utils.h"
26 #include "content/public/test/mock_render_process_host.h"
27 #include "content/public/test/test_notification_tracker.h"
28 #include "content/test/test_content_browser_client.h"
29 #include "content/test/test_content_client.h"
30 #include "content/test/test_render_view_host.h"
31 #include "content/test/test_web_contents.h"
32 #include "testing/gtest/include/gtest/gtest.h"
37 class RenderFrameHostManagerTestWebUIControllerFactory
38 : public WebUIControllerFactory {
40 RenderFrameHostManagerTestWebUIControllerFactory()
41 : should_create_webui_(false) {
43 virtual ~RenderFrameHostManagerTestWebUIControllerFactory() {}
45 void set_should_create_webui(bool should_create_webui) {
46 should_create_webui_ = should_create_webui;
49 // WebUIFactory implementation.
50 virtual WebUIController* CreateWebUIControllerForURL(
51 WebUI* web_ui, const GURL& url) const OVERRIDE {
52 if (!(should_create_webui_ && HasWebUIScheme(url)))
54 return new WebUIController(web_ui);
57 virtual WebUI::TypeID GetWebUIType(BrowserContext* browser_context,
58 const GURL& url) const OVERRIDE {
59 return WebUI::kNoWebUI;
62 virtual bool UseWebUIForURL(BrowserContext* browser_context,
63 const GURL& url) const OVERRIDE {
64 return HasWebUIScheme(url);
67 virtual bool UseWebUIBindingsForURL(BrowserContext* browser_context,
68 const GURL& url) const OVERRIDE {
69 return HasWebUIScheme(url);
73 bool should_create_webui_;
75 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostManagerTestWebUIControllerFactory);
78 class BeforeUnloadFiredWebContentsDelegate : public WebContentsDelegate {
80 BeforeUnloadFiredWebContentsDelegate() {}
81 virtual ~BeforeUnloadFiredWebContentsDelegate() {}
83 virtual void BeforeUnloadFired(WebContents* web_contents,
85 bool* proceed_to_fire_unload) OVERRIDE {
86 *proceed_to_fire_unload = proceed;
90 DISALLOW_COPY_AND_ASSIGN(BeforeUnloadFiredWebContentsDelegate);
95 class RenderFrameHostManagerTest
96 : public RenderViewHostImplTestHarness {
98 virtual void SetUp() OVERRIDE {
99 RenderViewHostImplTestHarness::SetUp();
100 WebUIControllerFactory::RegisterFactory(&factory_);
103 virtual void TearDown() OVERRIDE {
104 RenderViewHostImplTestHarness::TearDown();
105 WebUIControllerFactory::UnregisterFactoryForTesting(&factory_);
108 void set_should_create_webui(bool should_create_webui) {
109 factory_.set_should_create_webui(should_create_webui);
112 void NavigateActiveAndCommit(const GURL& url) {
113 // Note: we navigate the active RenderViewHost because previous navigations
114 // won't have committed yet, so NavigateAndCommit does the wrong thing
116 controller().LoadURL(url, Referrer(), PAGE_TRANSITION_LINK, std::string());
117 TestRenderViewHost* old_rvh = test_rvh();
119 // Simulate the ShouldClose_ACK that is received from the current renderer
120 // for a cross-site navigation.
121 if (old_rvh != active_rvh())
122 old_rvh->SendShouldCloseACK(true);
124 // Commit the navigation with a new page ID.
125 int32 max_page_id = contents()->GetMaxPageIDForSiteInstance(
126 active_rvh()->GetSiteInstance());
128 // Simulate the SwapOut_ACK that fires if you commit a cross-site
130 if (old_rvh != active_rvh())
131 old_rvh->OnSwappedOut(false);
133 active_test_rvh()->SendNavigate(max_page_id + 1, url);
136 bool ShouldSwapProcesses(RenderFrameHostManager* manager,
137 const NavigationEntryImpl* current_entry,
138 const NavigationEntryImpl* new_entry) const {
139 return manager->ShouldSwapBrowsingInstancesForNavigation(current_entry,
143 // Creates a test RenderViewHost that's swapped out.
144 TestRenderViewHost* CreateSwappedOutRenderViewHost() {
145 const GURL kChromeURL("chrome://foo");
146 const GURL kDestUrl("http://www.google.com/");
148 // Navigate our first tab to a chrome url and then to the destination.
149 NavigateActiveAndCommit(kChromeURL);
150 TestRenderViewHost* ntp_rvh = static_cast<TestRenderViewHost*>(
151 contents()->GetRenderManagerForTesting()->current_host());
153 // Navigate to a cross-site URL.
154 contents()->GetController().LoadURL(
155 kDestUrl, Referrer(), PAGE_TRANSITION_LINK, std::string());
156 EXPECT_TRUE(contents()->cross_navigation_pending());
158 // Manually increase the number of active views in the
159 // SiteInstance that ntp_rvh belongs to, to prevent it from being
160 // destroyed when it gets swapped out.
161 static_cast<SiteInstanceImpl*>(ntp_rvh->GetSiteInstance())->
162 increment_active_view_count();
164 TestRenderViewHost* dest_rvh = static_cast<TestRenderViewHost*>(
165 contents()->GetRenderManagerForTesting()->pending_render_view_host());
167 EXPECT_NE(ntp_rvh, dest_rvh);
169 // BeforeUnload finishes.
170 ntp_rvh->SendShouldCloseACK(true);
172 // Assume SwapOutACK times out, so the dest_rvh proceeds and commits.
173 dest_rvh->SendNavigate(101, kDestUrl);
175 EXPECT_TRUE(ntp_rvh->is_swapped_out());
180 RenderFrameHostManagerTestWebUIControllerFactory factory_;
183 // Tests that when you navigate from a chrome:// url to another page, and
184 // then do that same thing in another tab, that the two resulting pages have
185 // different SiteInstances, BrowsingInstances, and RenderProcessHosts. This is
186 // a regression test for bug 9364.
187 TEST_F(RenderFrameHostManagerTest, NewTabPageProcesses) {
188 set_should_create_webui(true);
189 const GURL kChromeUrl("chrome://foo");
190 const GURL kDestUrl("http://www.google.com/");
192 // Navigate our first tab to the chrome url and then to the destination,
193 // ensuring we grant bindings to the chrome URL.
194 NavigateActiveAndCommit(kChromeUrl);
195 EXPECT_TRUE(active_rvh()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
196 NavigateActiveAndCommit(kDestUrl);
198 // Make a second tab.
199 scoped_ptr<TestWebContents> contents2(
200 TestWebContents::Create(browser_context(), NULL));
202 // Load the two URLs in the second tab. Note that the first navigation creates
203 // a RVH that's not pending (since there is no cross-site transition), so
204 // we use the committed one.
205 contents2->GetController().LoadURL(
206 kChromeUrl, Referrer(), PAGE_TRANSITION_LINK, std::string());
207 TestRenderViewHost* ntp_rvh2 = static_cast<TestRenderViewHost*>(
208 contents2->GetRenderManagerForTesting()->current_host());
209 EXPECT_FALSE(contents2->cross_navigation_pending());
210 ntp_rvh2->SendNavigate(100, kChromeUrl);
212 // The second one is the opposite, creating a cross-site transition and
213 // requiring a beforeunload ack.
214 contents2->GetController().LoadURL(
215 kDestUrl, Referrer(), PAGE_TRANSITION_LINK, std::string());
216 EXPECT_TRUE(contents2->cross_navigation_pending());
217 TestRenderViewHost* dest_rvh2 = static_cast<TestRenderViewHost*>(
218 contents2->GetRenderManagerForTesting()->pending_render_view_host());
219 ASSERT_TRUE(dest_rvh2);
221 ntp_rvh2->SendShouldCloseACK(true);
222 ntp_rvh2->OnSwappedOut(false);
223 dest_rvh2->SendNavigate(101, kDestUrl);
225 // The two RVH's should be different in every way.
226 EXPECT_NE(active_rvh()->GetProcess(), dest_rvh2->GetProcess());
227 EXPECT_NE(active_rvh()->GetSiteInstance(), dest_rvh2->GetSiteInstance());
228 EXPECT_FALSE(active_rvh()->GetSiteInstance()->IsRelatedSiteInstance(
229 dest_rvh2->GetSiteInstance()));
231 // Navigate both to the new tab page, and verify that they share a
232 // RenderProcessHost (not a SiteInstance).
233 NavigateActiveAndCommit(kChromeUrl);
235 contents2->GetController().LoadURL(
236 kChromeUrl, Referrer(), PAGE_TRANSITION_LINK, std::string());
237 dest_rvh2->SendShouldCloseACK(true);
238 dest_rvh2->OnSwappedOut(false);
239 static_cast<TestRenderViewHost*>(contents2->GetRenderManagerForTesting()->
240 pending_render_view_host())->SendNavigate(102, kChromeUrl);
242 EXPECT_NE(active_rvh()->GetSiteInstance(),
243 contents2->GetRenderViewHost()->GetSiteInstance());
244 EXPECT_EQ(active_rvh()->GetSiteInstance()->GetProcess(),
245 contents2->GetRenderViewHost()->GetSiteInstance()->GetProcess());
248 // Ensure that the browser ignores most IPC messages that arrive from a
249 // RenderViewHost that has been swapped out. We do not want to take
250 // action on requests from a non-active renderer. The main exception is
251 // for synchronous messages, which cannot be ignored without leaving the
252 // renderer in a stuck state. See http://crbug.com/93427.
253 TEST_F(RenderFrameHostManagerTest, FilterMessagesWhileSwappedOut) {
254 const GURL kChromeURL("chrome://foo");
255 const GURL kDestUrl("http://www.google.com/");
257 // Navigate our first tab to a chrome url and then to the destination.
258 NavigateActiveAndCommit(kChromeURL);
259 TestRenderViewHost* ntp_rvh = static_cast<TestRenderViewHost*>(
260 contents()->GetRenderManagerForTesting()->current_host());
262 // Send an update title message and make sure it works.
263 const base::string16 ntp_title = base::ASCIIToUTF16("NTP Title");
264 blink::WebTextDirection direction = blink::WebTextDirectionLeftToRight;
265 EXPECT_TRUE(ntp_rvh->OnMessageReceived(
266 ViewHostMsg_UpdateTitle(rvh()->GetRoutingID(), 0, ntp_title, direction)));
267 EXPECT_EQ(ntp_title, contents()->GetTitle());
269 // Navigate to a cross-site URL.
270 contents()->GetController().LoadURL(
271 kDestUrl, Referrer(), PAGE_TRANSITION_LINK, std::string());
272 EXPECT_TRUE(contents()->cross_navigation_pending());
273 TestRenderViewHost* dest_rvh = static_cast<TestRenderViewHost*>(
274 contents()->GetRenderManagerForTesting()->pending_render_view_host());
275 ASSERT_TRUE(dest_rvh);
276 EXPECT_NE(ntp_rvh, dest_rvh);
278 // Create one more view in the same SiteInstance where dest_rvh2
279 // exists so that it doesn't get deleted on navigation to another
281 static_cast<SiteInstanceImpl*>(ntp_rvh->GetSiteInstance())->
282 increment_active_view_count();
284 // BeforeUnload finishes.
285 ntp_rvh->SendShouldCloseACK(true);
287 // Assume SwapOutACK times out, so the dest_rvh proceeds and commits.
288 dest_rvh->SendNavigate(101, kDestUrl);
290 // The new RVH should be able to update its title.
291 const base::string16 dest_title = base::ASCIIToUTF16("Google");
292 EXPECT_TRUE(dest_rvh->OnMessageReceived(
293 ViewHostMsg_UpdateTitle(rvh()->GetRoutingID(), 101, dest_title,
295 EXPECT_EQ(dest_title, contents()->GetTitle());
297 // The old renderer, being slow, now updates the title. It should be filtered
298 // out and not take effect.
299 EXPECT_TRUE(ntp_rvh->is_swapped_out());
300 EXPECT_TRUE(ntp_rvh->OnMessageReceived(
301 ViewHostMsg_UpdateTitle(rvh()->GetRoutingID(), 0, ntp_title, direction)));
302 EXPECT_EQ(dest_title, contents()->GetTitle());
304 // We cannot filter out synchronous IPC messages, because the renderer would
305 // be left waiting for a reply. We pick RunBeforeUnloadConfirm as an example
306 // that can run easily within a unit test, and that needs to receive a reply
307 // without showing an actual dialog.
308 MockRenderProcessHost* ntp_process_host =
309 static_cast<MockRenderProcessHost*>(ntp_rvh->GetProcess());
310 ntp_process_host->sink().ClearMessages();
311 const base::string16 msg = base::ASCIIToUTF16("Message");
313 base::string16 unused;
314 ViewHostMsg_RunBeforeUnloadConfirm before_unload_msg(
315 rvh()->GetRoutingID(), kChromeURL, msg, false, &result, &unused);
316 // Enable pumping for check in BrowserMessageFilter::CheckCanDispatchOnUI.
317 before_unload_msg.EnableMessagePumping();
318 EXPECT_TRUE(ntp_rvh->OnMessageReceived(before_unload_msg));
319 EXPECT_TRUE(ntp_process_host->sink().GetUniqueMessageMatching(IPC_REPLY_ID));
321 // Also test RunJavaScriptMessage.
322 ntp_process_host->sink().ClearMessages();
323 ViewHostMsg_RunJavaScriptMessage js_msg(
324 rvh()->GetRoutingID(), msg, msg, kChromeURL,
325 JAVASCRIPT_MESSAGE_TYPE_CONFIRM, &result, &unused);
326 js_msg.EnableMessagePumping();
327 EXPECT_TRUE(ntp_rvh->OnMessageReceived(js_msg));
328 EXPECT_TRUE(ntp_process_host->sink().GetUniqueMessageMatching(IPC_REPLY_ID));
331 TEST_F(RenderFrameHostManagerTest, WhiteListSwapCompositorFrame) {
332 TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost();
333 TestRenderWidgetHostView* swapped_out_rwhv =
334 static_cast<TestRenderWidgetHostView*>(swapped_out_rvh->GetView());
335 EXPECT_FALSE(swapped_out_rwhv->did_swap_compositor_frame());
337 MockRenderProcessHost* process_host =
338 static_cast<MockRenderProcessHost*>(swapped_out_rvh->GetProcess());
339 process_host->sink().ClearMessages();
341 cc::CompositorFrame frame;
342 ViewHostMsg_SwapCompositorFrame msg(rvh()->GetRoutingID(), 0, frame);
344 EXPECT_TRUE(swapped_out_rvh->OnMessageReceived(msg));
345 EXPECT_TRUE(swapped_out_rwhv->did_swap_compositor_frame());
348 TEST_F(RenderFrameHostManagerTest, WhiteListDidActivateAcceleratedCompositing) {
349 TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost();
351 MockRenderProcessHost* process_host =
352 static_cast<MockRenderProcessHost*>(swapped_out_rvh->GetProcess());
353 process_host->sink().ClearMessages();
354 ViewHostMsg_DidActivateAcceleratedCompositing msg(
355 rvh()->GetRoutingID(), true);
356 EXPECT_TRUE(swapped_out_rvh->OnMessageReceived(msg));
357 EXPECT_TRUE(swapped_out_rvh->is_accelerated_compositing_active());
360 // Test if RenderViewHost::GetRenderWidgetHosts() only returns active
362 TEST_F(RenderFrameHostManagerTest, GetRenderWidgetHostsReturnsActiveViews) {
363 TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost();
364 EXPECT_TRUE(swapped_out_rvh->is_swapped_out());
366 scoped_ptr<RenderWidgetHostIterator> widgets(
367 RenderWidgetHost::GetRenderWidgetHosts());
368 // We know that there is the only one active widget. Another view is
369 // now swapped out, so the swapped out view is not included in the
371 RenderWidgetHost* widget = widgets->GetNextHost();
372 EXPECT_FALSE(widgets->GetNextHost());
373 RenderViewHost* rvh = RenderViewHost::From(widget);
374 EXPECT_FALSE(static_cast<RenderViewHostImpl*>(rvh)->is_swapped_out());
377 // Test if RenderViewHost::GetRenderWidgetHosts() returns a subset of
378 // RenderViewHostImpl::GetAllRenderWidgetHosts().
379 // RenderViewHost::GetRenderWidgetHosts() returns only active widgets, but
380 // RenderViewHostImpl::GetAllRenderWidgetHosts() returns everything
381 // including swapped out ones.
382 TEST_F(RenderFrameHostManagerTest,
383 GetRenderWidgetHostsWithinGetAllRenderWidgetHosts) {
384 TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost();
385 EXPECT_TRUE(swapped_out_rvh->is_swapped_out());
387 scoped_ptr<RenderWidgetHostIterator> widgets(
388 RenderWidgetHost::GetRenderWidgetHosts());
390 while (RenderWidgetHost* w = widgets->GetNextHost()) {
392 scoped_ptr<RenderWidgetHostIterator> all_widgets(
393 RenderWidgetHostImpl::GetAllRenderWidgetHosts());
394 while (RenderWidgetHost* widget = all_widgets->GetNextHost()) {
404 // Test if SiteInstanceImpl::active_view_count() is correctly updated
405 // as views in a SiteInstance get swapped out and in.
406 TEST_F(RenderFrameHostManagerTest, ActiveViewCountWhileSwappingInandOut) {
407 const GURL kUrl1("http://www.google.com/");
408 const GURL kUrl2("http://www.chromium.org/");
410 // Navigate to an initial URL.
411 contents()->NavigateAndCommit(kUrl1);
412 TestRenderViewHost* rvh1 = test_rvh();
414 SiteInstanceImpl* instance1 =
415 static_cast<SiteInstanceImpl*>(rvh1->GetSiteInstance());
416 EXPECT_EQ(instance1->active_view_count(), 1U);
418 // Create 2 new tabs and simulate them being the opener chain for the main
419 // tab. They should be in the same SiteInstance.
420 scoped_ptr<TestWebContents> opener1(
421 TestWebContents::Create(browser_context(), instance1));
422 contents()->SetOpener(opener1.get());
424 scoped_ptr<TestWebContents> opener2(
425 TestWebContents::Create(browser_context(), instance1));
426 opener1->SetOpener(opener2.get());
428 EXPECT_EQ(instance1->active_view_count(), 3U);
430 // Navigate to a cross-site URL (different SiteInstance but same
431 // BrowsingInstance).
432 contents()->NavigateAndCommit(kUrl2);
433 TestRenderViewHost* rvh2 = test_rvh();
434 SiteInstanceImpl* instance2 =
435 static_cast<SiteInstanceImpl*>(rvh2->GetSiteInstance());
437 // rvh2 is on chromium.org which is different from google.com on
438 // which other tabs are.
439 EXPECT_EQ(instance2->active_view_count(), 1U);
441 // There are two active views on google.com now.
442 EXPECT_EQ(instance1->active_view_count(), 2U);
444 // Navigate to the original origin (google.com).
445 contents()->NavigateAndCommit(kUrl1);
447 EXPECT_EQ(instance1->active_view_count(), 3U);
450 // This deletes a WebContents when the given RVH is deleted. This is
451 // only for testing whether deleting an RVH does not cause any UaF in
452 // other parts of the system. For now, this class is only used for the
453 // next test cases to detect the bug mentioned at
454 // http://crbug.com/259859.
455 class RenderViewHostDestroyer : public WebContentsObserver {
457 RenderViewHostDestroyer(RenderViewHost* render_view_host,
458 WebContents* web_contents)
459 : WebContentsObserver(WebContents::FromRenderViewHost(render_view_host)),
460 render_view_host_(render_view_host),
461 web_contents_(web_contents) {}
463 virtual void RenderViewDeleted(
464 RenderViewHost* render_view_host) OVERRIDE {
465 if (render_view_host == render_view_host_)
466 delete web_contents_;
470 RenderViewHost* render_view_host_;
471 WebContents* web_contents_;
473 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestroyer);
476 // Test if ShutdownRenderViewHostsInSiteInstance() does not touch any
477 // RenderWidget that has been freed while deleting a RenderViewHost in
478 // a previous iteration. This is a regression test for
479 // http://crbug.com/259859.
480 TEST_F(RenderFrameHostManagerTest,
481 DetectUseAfterFreeInShutdownRenderViewHostsInSiteInstance) {
482 const GURL kChromeURL("chrome://newtab");
483 const GURL kUrl1("http://www.google.com");
484 const GURL kUrl2("http://www.chromium.org");
486 // Navigate our first tab to a chrome url and then to the destination.
487 NavigateActiveAndCommit(kChromeURL);
488 TestRenderViewHost* ntp_rvh = static_cast<TestRenderViewHost*>(
489 contents()->GetRenderManagerForTesting()->current_host());
491 // Create one more tab and navigate to kUrl1. web_contents is not
492 // wrapped as scoped_ptr since it intentionally deleted by destroyer
493 // below as part of this test.
494 TestWebContents* web_contents =
495 TestWebContents::Create(browser_context(), ntp_rvh->GetSiteInstance());
496 web_contents->NavigateAndCommit(kUrl1);
497 RenderViewHostDestroyer destroyer(ntp_rvh, web_contents);
499 // This causes the first tab to navigate to kUrl2, which destroys
500 // the ntp_rvh in ShutdownRenderViewHostsInSiteInstance(). When
501 // ntp_rvh is destroyed, it also destroys the RVHs in web_contents
502 // too. This can test whether
503 // SiteInstanceImpl::ShutdownRenderViewHostsInSiteInstance() can
504 // touch any object freed in this way or not while iterating through
506 contents()->NavigateAndCommit(kUrl2);
509 // When there is an error with the specified page, renderer exits view-source
510 // mode. See WebFrameImpl::DidFail(). We check by this test that
511 // EnableViewSourceMode message is sent on every navigation regardless
512 // RenderView is being newly created or reused.
513 TEST_F(RenderFrameHostManagerTest, AlwaysSendEnableViewSourceMode) {
514 const GURL kChromeUrl("chrome://foo");
515 const GURL kUrl("view-source:http://foo");
517 // We have to navigate to some page at first since without this, the first
518 // navigation will reuse the SiteInstance created by Init(), and the second
519 // one will create a new SiteInstance. Because current_instance and
520 // new_instance will be different, a new RenderViewHost will be created for
521 // the second navigation. We have to avoid this in order to exercise the
522 // target code patch.
523 NavigateActiveAndCommit(kChromeUrl);
526 controller().LoadURL(
527 kUrl, Referrer(), PAGE_TRANSITION_TYPED, std::string());
528 // Simulate response from RenderView for FirePageBeforeUnload.
529 base::TimeTicks now = base::TimeTicks::Now();
530 test_rvh()->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(
531 rvh()->GetRoutingID(), true, now, now));
532 ASSERT_TRUE(pending_rvh()); // New pending RenderViewHost will be created.
533 RenderViewHost* last_rvh = pending_rvh();
534 int32 new_id = contents()->GetMaxPageIDForSiteInstance(
535 active_rvh()->GetSiteInstance()) + 1;
536 pending_test_rvh()->SendNavigate(new_id, kUrl);
537 EXPECT_EQ(controller().GetLastCommittedEntryIndex(), 1);
538 ASSERT_TRUE(controller().GetLastCommittedEntry());
539 EXPECT_TRUE(kUrl == controller().GetLastCommittedEntry()->GetURL());
540 EXPECT_FALSE(controller().GetPendingEntry());
541 // Because we're using TestWebContents and TestRenderViewHost in this
542 // unittest, no one calls WebContentsImpl::RenderViewCreated(). So, we see no
543 // EnableViewSourceMode message, here.
545 // Clear queued messages before load.
546 process()->sink().ClearMessages();
548 controller().LoadURL(
549 kUrl, Referrer(), PAGE_TRANSITION_TYPED, std::string());
550 // The same RenderViewHost should be reused.
551 EXPECT_FALSE(pending_rvh());
552 EXPECT_TRUE(last_rvh == rvh());
553 test_rvh()->SendNavigate(new_id, kUrl); // The same page_id returned.
554 EXPECT_EQ(controller().GetLastCommittedEntryIndex(), 1);
555 EXPECT_FALSE(controller().GetPendingEntry());
556 // New message should be sent out to make sure to enter view-source mode.
557 EXPECT_TRUE(process()->sink().GetUniqueMessageMatching(
558 ViewMsg_EnableViewSourceMode::ID));
561 // Tests the Init function by checking the initial RenderViewHost.
562 TEST_F(RenderFrameHostManagerTest, Init) {
563 // Using TestBrowserContext.
564 SiteInstanceImpl* instance =
565 static_cast<SiteInstanceImpl*>(SiteInstance::Create(browser_context()));
566 EXPECT_FALSE(instance->HasSite());
568 scoped_ptr<TestWebContents> web_contents(
569 TestWebContents::Create(browser_context(), instance));
570 FrameTree tree(web_contents->GetFrameTree()->root()->navigator(),
571 web_contents.get(), web_contents.get(),
572 web_contents.get(), web_contents.get());
573 RenderFrameHostManager* manager = tree.root()->render_manager();
575 manager->Init(browser_context(), instance, MSG_ROUTING_NONE,
578 RenderViewHostImpl* rvh = manager->current_host();
579 RenderFrameHostImpl* rfh = manager->current_frame_host();
582 EXPECT_EQ(rvh, rfh->render_view_host());
583 EXPECT_EQ(instance, rvh->GetSiteInstance());
584 EXPECT_EQ(web_contents.get(), rvh->GetDelegate());
585 EXPECT_EQ(web_contents.get(), rfh->delegate());
586 EXPECT_TRUE(manager->GetRenderWidgetHostView());
587 EXPECT_FALSE(manager->pending_render_view_host());
590 // Tests the Navigate function. We navigate three sites consecutively and check
591 // how the pending/committed RenderViewHost are modified.
592 TEST_F(RenderFrameHostManagerTest, Navigate) {
593 TestNotificationTracker notifications;
595 SiteInstance* instance = SiteInstance::Create(browser_context());
597 scoped_ptr<TestWebContents> web_contents(
598 TestWebContents::Create(browser_context(), instance));
599 notifications.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
600 Source<WebContents>(web_contents.get()));
603 FrameTree tree(web_contents->GetFrameTree()->root()->navigator(),
604 web_contents.get(), web_contents.get(),
605 web_contents.get(), web_contents.get());
606 RenderFrameHostManager* manager = tree.root()->render_manager();
608 manager->Init(browser_context(), instance, MSG_ROUTING_NONE,
611 RenderFrameHostImpl* host;
613 // 1) The first navigation. --------------------------
614 const GURL kUrl1("http://www.google.com/");
615 NavigationEntryImpl entry1(
616 NULL /* instance */, -1 /* page_id */, kUrl1, Referrer(),
617 base::string16() /* title */, PAGE_TRANSITION_TYPED,
618 false /* is_renderer_init */);
619 host = manager->Navigate(entry1);
621 // The RenderFrameHost created in Init will be reused.
622 EXPECT_TRUE(host == manager->current_frame_host());
623 EXPECT_FALSE(manager->pending_frame_host());
626 manager->DidNavigateMainFrame(host->render_view_host());
627 // Commit to SiteInstance should be delayed until RenderView commit.
628 EXPECT_TRUE(host == manager->current_frame_host());
630 EXPECT_FALSE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
632 static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->SetSite(kUrl1);
634 // 2) Navigate to next site. -------------------------
635 const GURL kUrl2("http://www.google.com/foo");
636 NavigationEntryImpl entry2(
637 NULL /* instance */, -1 /* page_id */, kUrl2,
638 Referrer(kUrl1, blink::WebReferrerPolicyDefault),
639 base::string16() /* title */, PAGE_TRANSITION_LINK,
640 true /* is_renderer_init */);
641 host = manager->Navigate(entry2);
643 // The RenderFrameHost created in Init will be reused.
644 EXPECT_TRUE(host == manager->current_frame_host());
645 EXPECT_FALSE(manager->pending_frame_host());
648 manager->DidNavigateMainFrame(host->render_view_host());
649 EXPECT_TRUE(host == manager->current_frame_host());
651 EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
654 // 3) Cross-site navigate to next site. --------------
655 const GURL kUrl3("http://webkit.org/");
656 NavigationEntryImpl entry3(
657 NULL /* instance */, -1 /* page_id */, kUrl3,
658 Referrer(kUrl2, blink::WebReferrerPolicyDefault),
659 base::string16() /* title */, PAGE_TRANSITION_LINK,
660 false /* is_renderer_init */);
661 host = manager->Navigate(entry3);
663 // A new RenderFrameHost should be created.
664 EXPECT_TRUE(manager->pending_frame_host());
665 ASSERT_EQ(host, manager->pending_frame_host());
667 notifications.Reset();
670 manager->DidNavigateMainFrame(manager->pending_render_view_host());
671 EXPECT_TRUE(host == manager->current_frame_host());
673 EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
675 // Check the pending RenderFrameHost has been committed.
676 EXPECT_FALSE(manager->pending_frame_host());
678 // We should observe a notification.
680 notifications.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED));
683 // Tests the Navigate function. In this unit test we verify that the Navigate
684 // function can handle a new navigation event before the previous navigation
685 // has been committed. This is also a regression test for
686 // http://crbug.com/104600.
687 TEST_F(RenderFrameHostManagerTest, NavigateWithEarlyReNavigation) {
688 TestNotificationTracker notifications;
690 SiteInstance* instance = SiteInstance::Create(browser_context());
692 scoped_ptr<TestWebContents> web_contents(
693 TestWebContents::Create(browser_context(), instance));
694 notifications.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
695 Source<WebContents>(web_contents.get()));
698 FrameTree tree(web_contents->GetFrameTree()->root()->navigator(),
699 web_contents.get(), web_contents.get(),
700 web_contents.get(), web_contents.get());
701 RenderFrameHostManager* manager = tree.root()->render_manager();
703 manager->Init(browser_context(), instance, MSG_ROUTING_NONE,
706 // 1) The first navigation. --------------------------
707 const GURL kUrl1("http://www.google.com/");
708 NavigationEntryImpl entry1(NULL /* instance */, -1 /* page_id */, kUrl1,
709 Referrer(), base::string16() /* title */,
710 PAGE_TRANSITION_TYPED,
711 false /* is_renderer_init */);
712 RenderFrameHostImpl* host = manager->Navigate(entry1);
714 // The RenderFrameHost created in Init will be reused.
715 EXPECT_TRUE(host == manager->current_frame_host());
716 EXPECT_FALSE(manager->pending_frame_host());
718 // We should observe a notification.
720 notifications.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED));
721 notifications.Reset();
724 manager->DidNavigateMainFrame(host->render_view_host());
726 // Commit to SiteInstance should be delayed until RenderView commit.
727 EXPECT_TRUE(host == manager->current_frame_host());
729 EXPECT_FALSE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
731 static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->SetSite(kUrl1);
733 // 2) Cross-site navigate to next site. -------------------------
734 const GURL kUrl2("http://www.example.com");
735 NavigationEntryImpl entry2(
736 NULL /* instance */, -1 /* page_id */, kUrl2, Referrer(),
737 base::string16() /* title */, PAGE_TRANSITION_TYPED,
738 false /* is_renderer_init */);
739 RenderFrameHostImpl* host2 = manager->Navigate(entry2);
740 int host2_process_id = host2->GetProcess()->GetID();
742 // A new RenderFrameHost should be created.
743 EXPECT_TRUE(manager->pending_frame_host());
744 ASSERT_EQ(host2, manager->pending_frame_host());
745 EXPECT_NE(host2, host);
747 // Check that the navigation is still suspended because the old RVH
748 // is not swapped out, yet.
749 EXPECT_TRUE(host2->render_view_host()->are_navigations_suspended());
750 MockRenderProcessHost* test_process_host2 =
751 static_cast<MockRenderProcessHost*>(host2->GetProcess());
752 test_process_host2->sink().ClearMessages();
753 host2->render_view_host()->NavigateToURL(kUrl2);
754 EXPECT_FALSE(test_process_host2->sink().GetUniqueMessageMatching(
755 ViewMsg_Navigate::ID));
757 // Allow closing the current Render View (precondition for swapping out
758 // the RVH): Simulate response from RenderView for ViewMsg_ShouldClose sent by
759 // FirePageBeforeUnload.
760 TestRenderViewHost* test_host =
761 static_cast<TestRenderViewHost*>(host->render_view_host());
762 MockRenderProcessHost* test_process_host =
763 static_cast<MockRenderProcessHost*>(test_host->GetProcess());
764 EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching(
765 ViewMsg_ShouldClose::ID));
766 test_host->SendShouldCloseACK(true);
768 // CrossSiteResourceHandler::StartCrossSiteTransition triggers a
769 // call of RenderFrameHostManager::SwapOutOldPage before
770 // RenderFrameHostManager::DidNavigateMainFrame is called.
771 // The RVH is not swapped out until the commit.
772 manager->SwapOutOldPage();
773 EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching(
774 ViewMsg_SwapOut::ID));
775 test_host->OnSwappedOut(false);
777 EXPECT_EQ(host, manager->current_frame_host());
778 EXPECT_FALSE(manager->current_frame_host()->is_swapped_out());
779 EXPECT_EQ(host2, manager->pending_frame_host());
780 // There should be still no navigation messages being sent.
781 EXPECT_FALSE(test_process_host2->sink().GetUniqueMessageMatching(
782 ViewMsg_Navigate::ID));
784 // 3) Cross-site navigate to next site before 2) has committed. --------------
785 const GURL kUrl3("http://webkit.org/");
786 NavigationEntryImpl entry3(NULL /* instance */, -1 /* page_id */, kUrl3,
787 Referrer(), base::string16() /* title */,
788 PAGE_TRANSITION_TYPED,
789 false /* is_renderer_init */);
790 test_process_host->sink().ClearMessages();
791 RenderFrameHostImpl* host3 = manager->Navigate(entry3);
793 // A new RenderFrameHost should be created. host2 is now deleted.
794 EXPECT_TRUE(manager->pending_frame_host());
795 ASSERT_EQ(host3, manager->pending_frame_host());
796 EXPECT_NE(host3, host);
797 EXPECT_NE(host3->GetProcess()->GetID(), host2_process_id);
799 // Navigations in the new RVH should be suspended, which is ok because the
800 // old RVH is not yet swapped out and can respond to a second beforeunload
802 EXPECT_TRUE(static_cast<RenderViewHostImpl*>(
803 host3->render_view_host())->are_navigations_suspended());
804 EXPECT_EQ(host, manager->current_frame_host());
805 EXPECT_FALSE(manager->current_frame_host()->is_swapped_out());
807 // Simulate a response to the second beforeunload request.
808 EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching(
809 ViewMsg_ShouldClose::ID));
810 test_host->SendShouldCloseACK(true);
812 // CrossSiteResourceHandler::StartCrossSiteTransition triggers a
813 // call of RenderFrameHostManager::SwapOutOldPage before
814 // RenderFrameHostManager::DidNavigateMainFrame is called.
815 // The RVH is not swapped out until the commit.
816 manager->SwapOutOldPage();
817 EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching(
818 ViewMsg_SwapOut::ID));
819 test_host->OnSwappedOut(false);
822 manager->DidNavigateMainFrame(host3->render_view_host());
823 EXPECT_TRUE(host3 == manager->current_frame_host());
825 EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host3->GetSiteInstance())->
827 // Check the pending RenderFrameHost has been committed.
828 EXPECT_FALSE(manager->pending_frame_host());
830 // We should observe a notification.
832 notifications.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED));
835 // Tests WebUI creation.
836 TEST_F(RenderFrameHostManagerTest, WebUI) {
837 set_should_create_webui(true);
838 SiteInstance* instance = SiteInstance::Create(browser_context());
840 scoped_ptr<TestWebContents> web_contents(
841 TestWebContents::Create(browser_context(), instance));
842 FrameTree tree(web_contents->GetFrameTree()->root()->navigator(),
843 web_contents.get(), web_contents.get(),
844 web_contents.get(), web_contents.get());
845 RenderFrameHostManager* manager = tree.root()->render_manager();
847 manager->Init(browser_context(), instance, MSG_ROUTING_NONE,
849 EXPECT_FALSE(manager->current_host()->IsRenderViewLive());
851 const GURL kUrl("chrome://foo");
852 NavigationEntryImpl entry(NULL /* instance */, -1 /* page_id */, kUrl,
853 Referrer(), base::string16() /* title */,
854 PAGE_TRANSITION_TYPED,
855 false /* is_renderer_init */);
856 RenderFrameHostImpl* host = manager->Navigate(entry);
858 // We commit the pending RenderFrameHost immediately because the previous
859 // RenderFrameHost was not live. We test a case where it is live in
862 EXPECT_EQ(host, manager->current_frame_host());
863 EXPECT_FALSE(manager->pending_frame_host());
865 // It's important that the site instance get set on the Web UI page as soon
866 // as the navigation starts, rather than lazily after it commits, so we don't
867 // try to re-use the SiteInstance/process for non Web UI things that may
868 // get loaded in between.
869 EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
871 EXPECT_EQ(kUrl, host->GetSiteInstance()->GetSiteURL());
873 // The Web UI is committed immediately because the RenderViewHost has not been
874 // used yet. UpdateRendererStateForNavigate() took the short cut path.
875 EXPECT_FALSE(manager->pending_web_ui());
876 EXPECT_TRUE(manager->web_ui());
879 manager->DidNavigateMainFrame(host->render_view_host());
881 host->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
884 // Tests that we can open a WebUI link in a new tab from a WebUI page and still
885 // grant the correct bindings. http://crbug.com/189101.
886 TEST_F(RenderFrameHostManagerTest, WebUIInNewTab) {
887 set_should_create_webui(true);
888 SiteInstance* blank_instance = SiteInstance::Create(browser_context());
890 // Create a blank tab.
891 scoped_ptr<TestWebContents> web_contents1(
892 TestWebContents::Create(browser_context(), blank_instance));
893 FrameTree tree1(web_contents1->GetFrameTree()->root()->navigator(),
894 web_contents1.get(), web_contents1.get(),
895 web_contents1.get(), web_contents1.get());
896 RenderFrameHostManager* manager1 = tree1.root()->render_manager();
898 browser_context(), blank_instance, MSG_ROUTING_NONE, MSG_ROUTING_NONE);
899 // Test the case that new RVH is considered live.
900 manager1->current_host()->CreateRenderView(base::string16(), -1, -1);
902 // Navigate to a WebUI page.
903 const GURL kUrl1("chrome://foo");
904 NavigationEntryImpl entry1(NULL /* instance */, -1 /* page_id */, kUrl1,
905 Referrer(), base::string16() /* title */,
906 PAGE_TRANSITION_TYPED,
907 false /* is_renderer_init */);
908 RenderFrameHostImpl* host1 = manager1->Navigate(entry1);
910 // We should have a pending navigation to the WebUI RenderViewHost.
911 // It should already have bindings.
912 EXPECT_EQ(host1, manager1->pending_frame_host());
913 EXPECT_NE(host1, manager1->current_frame_host());
915 host1->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
917 // Commit and ensure we still have bindings.
918 manager1->DidNavigateMainFrame(host1->render_view_host());
919 SiteInstance* webui_instance = host1->GetSiteInstance();
920 EXPECT_EQ(host1, manager1->current_frame_host());
922 host1->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
924 // Now simulate clicking a link that opens in a new tab.
925 scoped_ptr<TestWebContents> web_contents2(
926 TestWebContents::Create(browser_context(), webui_instance));
927 FrameTree tree2(web_contents2->GetFrameTree()->root()->navigator(),
928 web_contents2.get(), web_contents2.get(),
929 web_contents2.get(), web_contents2.get());
930 RenderFrameHostManager* manager2 = tree2.root()->render_manager();
932 browser_context(), webui_instance, MSG_ROUTING_NONE, MSG_ROUTING_NONE);
933 // Make sure the new RVH is considered live. This is usually done in
934 // RenderWidgetHost::Init when opening a new tab from a link.
935 manager2->current_host()->CreateRenderView(base::string16(), -1, -1);
937 const GURL kUrl2("chrome://foo/bar");
938 NavigationEntryImpl entry2(NULL /* instance */, -1 /* page_id */, kUrl2,
939 Referrer(), base::string16() /* title */,
940 PAGE_TRANSITION_LINK,
941 true /* is_renderer_init */);
942 RenderFrameHostImpl* host2 = manager2->Navigate(entry2);
944 // No cross-process transition happens because we are already in the right
945 // SiteInstance. We should grant bindings immediately.
946 EXPECT_EQ(host2, manager2->current_frame_host());
948 host2->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
950 manager2->DidNavigateMainFrame(host2->render_view_host());
953 // Tests that we don't end up in an inconsistent state if a page does a back and
954 // then reload. http://crbug.com/51680
955 TEST_F(RenderFrameHostManagerTest, PageDoesBackAndReload) {
956 const GURL kUrl1("http://www.google.com/");
957 const GURL kUrl2("http://www.evil-site.com/");
959 // Navigate to a safe site, then an evil site.
960 // This will switch RenderViewHosts. We cannot assert that the first and
961 // second RVHs are different, though, because the first one may be promptly
963 contents()->NavigateAndCommit(kUrl1);
964 contents()->NavigateAndCommit(kUrl2);
965 RenderViewHost* evil_rvh = contents()->GetRenderViewHost();
967 // Now let's simulate the evil page calling history.back().
968 contents()->OnGoToEntryAtOffset(-1);
969 // We should have a new pending RVH.
970 // Note that in this case, the navigation has not committed, so evil_rvh will
971 // not be deleted yet.
972 EXPECT_NE(evil_rvh, contents()->GetRenderManagerForTesting()->
973 pending_render_view_host());
975 // Before that RVH has committed, the evil page reloads itself.
976 ViewHostMsg_FrameNavigate_Params params;
979 params.transition = PAGE_TRANSITION_CLIENT_REDIRECT;
980 params.should_update_history = false;
981 params.gesture = NavigationGestureAuto;
982 params.was_within_same_page = false;
983 params.is_post = false;
984 params.page_state = PageState::CreateFromURL(kUrl2);
985 contents()->DidNavigate(evil_rvh, params);
987 // That should have cancelled the pending RVH, and the evil RVH should be the
989 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
990 pending_render_view_host() == NULL);
991 EXPECT_EQ(evil_rvh, contents()->GetRenderManagerForTesting()->current_host());
993 // Also we should not have a pending navigation entry.
994 EXPECT_TRUE(contents()->GetController().GetPendingEntry() == NULL);
995 NavigationEntry* entry = contents()->GetController().GetVisibleEntry();
996 ASSERT_TRUE(entry != NULL);
997 EXPECT_EQ(kUrl2, entry->GetURL());
1000 // Ensure that we can go back and forward even if a SwapOut ACK isn't received.
1001 // See http://crbug.com/93427.
1002 TEST_F(RenderFrameHostManagerTest, NavigateAfterMissingSwapOutACK) {
1003 const GURL kUrl1("http://www.google.com/");
1004 const GURL kUrl2("http://www.chromium.org/");
1006 // Navigate to two pages.
1007 contents()->NavigateAndCommit(kUrl1);
1008 TestRenderViewHost* rvh1 = test_rvh();
1010 // Keep active_view_count nonzero so that no swapped out views in
1011 // this SiteInstance get forcefully deleted.
1012 static_cast<SiteInstanceImpl*>(rvh1->GetSiteInstance())->
1013 increment_active_view_count();
1015 contents()->NavigateAndCommit(kUrl2);
1016 TestRenderViewHost* rvh2 = test_rvh();
1017 static_cast<SiteInstanceImpl*>(rvh2->GetSiteInstance())->
1018 increment_active_view_count();
1020 // Now go back, but suppose the SwapOut_ACK isn't received. This shouldn't
1021 // happen, but we have seen it when going back quickly across many entries
1022 // (http://crbug.com/93427).
1023 contents()->GetController().GoBack();
1024 EXPECT_TRUE(rvh2->is_waiting_for_beforeunload_ack());
1025 contents()->ProceedWithCrossSiteNavigation();
1026 EXPECT_FALSE(rvh2->is_waiting_for_beforeunload_ack());
1028 EXPECT_TRUE(rvh2->is_waiting_for_unload_ack());
1030 // The back navigation commits. We should proactively clear the
1031 // is_waiting_for_unload_ack state to be safe.
1032 const NavigationEntry* entry1 = contents()->GetController().GetPendingEntry();
1033 rvh1->SendNavigate(entry1->GetPageID(), entry1->GetURL());
1034 EXPECT_TRUE(rvh2->is_swapped_out());
1035 EXPECT_FALSE(rvh2->is_waiting_for_unload_ack());
1037 // We should be able to navigate forward.
1038 contents()->GetController().GoForward();
1039 contents()->ProceedWithCrossSiteNavigation();
1040 const NavigationEntry* entry2 = contents()->GetController().GetPendingEntry();
1041 rvh2->SendNavigate(entry2->GetPageID(), entry2->GetURL());
1042 EXPECT_EQ(rvh2, rvh());
1043 EXPECT_FALSE(rvh2->is_swapped_out());
1044 EXPECT_TRUE(rvh1->is_swapped_out());
1047 // Test that we create swapped out RVHs for the opener chain when navigating an
1048 // opened tab cross-process. This allows us to support certain cross-process
1049 // JavaScript calls (http://crbug.com/99202).
1050 TEST_F(RenderFrameHostManagerTest, CreateSwappedOutOpenerRVHs) {
1051 const GURL kUrl1("http://www.google.com/");
1052 const GURL kUrl2("http://www.chromium.org/");
1053 const GURL kChromeUrl("chrome://foo");
1055 // Navigate to an initial URL.
1056 contents()->NavigateAndCommit(kUrl1);
1057 RenderFrameHostManager* manager = contents()->GetRenderManagerForTesting();
1058 TestRenderViewHost* rvh1 = test_rvh();
1060 // Create 2 new tabs and simulate them being the opener chain for the main
1061 // tab. They should be in the same SiteInstance.
1062 scoped_ptr<TestWebContents> opener1(
1063 TestWebContents::Create(browser_context(), rvh1->GetSiteInstance()));
1064 RenderFrameHostManager* opener1_manager =
1065 opener1->GetRenderManagerForTesting();
1066 contents()->SetOpener(opener1.get());
1068 scoped_ptr<TestWebContents> opener2(
1069 TestWebContents::Create(browser_context(), rvh1->GetSiteInstance()));
1070 RenderFrameHostManager* opener2_manager =
1071 opener2->GetRenderManagerForTesting();
1072 opener1->SetOpener(opener2.get());
1074 // Navigate to a cross-site URL (different SiteInstance but same
1075 // BrowsingInstance).
1076 contents()->NavigateAndCommit(kUrl2);
1077 TestRenderViewHost* rvh2 = test_rvh();
1078 EXPECT_NE(rvh1->GetSiteInstance(), rvh2->GetSiteInstance());
1079 EXPECT_TRUE(rvh1->GetSiteInstance()->IsRelatedSiteInstance(
1080 rvh2->GetSiteInstance()));
1082 // Ensure rvh1 is placed on swapped out list of the current tab.
1083 EXPECT_TRUE(manager->IsRVHOnSwappedOutList(rvh1));
1085 manager->GetSwappedOutRenderViewHost(rvh1->GetSiteInstance()));
1087 // Ensure a swapped out RVH is created in the first opener tab.
1088 TestRenderViewHost* opener1_rvh = static_cast<TestRenderViewHost*>(
1089 opener1_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance()));
1090 EXPECT_TRUE(opener1_manager->IsRVHOnSwappedOutList(opener1_rvh));
1091 EXPECT_TRUE(opener1_rvh->is_swapped_out());
1093 // Ensure a swapped out RVH is created in the second opener tab.
1094 TestRenderViewHost* opener2_rvh = static_cast<TestRenderViewHost*>(
1095 opener2_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance()));
1096 EXPECT_TRUE(opener2_manager->IsRVHOnSwappedOutList(opener2_rvh));
1097 EXPECT_TRUE(opener2_rvh->is_swapped_out());
1099 // Navigate to a cross-BrowsingInstance URL.
1100 contents()->NavigateAndCommit(kChromeUrl);
1101 TestRenderViewHost* rvh3 = test_rvh();
1102 EXPECT_NE(rvh1->GetSiteInstance(), rvh3->GetSiteInstance());
1103 EXPECT_FALSE(rvh1->GetSiteInstance()->IsRelatedSiteInstance(
1104 rvh3->GetSiteInstance()));
1106 // No scripting is allowed across BrowsingInstances, so we should not create
1107 // swapped out RVHs for the opener chain in this case.
1108 EXPECT_FALSE(opener1_manager->GetSwappedOutRenderViewHost(
1109 rvh3->GetSiteInstance()));
1110 EXPECT_FALSE(opener2_manager->GetSwappedOutRenderViewHost(
1111 rvh3->GetSiteInstance()));
1114 // Test that we clean up swapped out RenderViewHosts when a process hosting
1115 // those associated RenderViews crashes. http://crbug.com/258993
1116 TEST_F(RenderFrameHostManagerTest, CleanUpSwappedOutRVHOnProcessCrash) {
1117 const GURL kUrl1("http://www.google.com/");
1118 const GURL kUrl2("http://www.chromium.org/");
1120 // Navigate to an initial URL.
1121 contents()->NavigateAndCommit(kUrl1);
1122 TestRenderViewHost* rvh1 = test_rvh();
1124 // Create a new tab as an opener for the main tab.
1125 scoped_ptr<TestWebContents> opener1(
1126 TestWebContents::Create(browser_context(), rvh1->GetSiteInstance()));
1127 RenderFrameHostManager* opener1_manager =
1128 opener1->GetRenderManagerForTesting();
1129 contents()->SetOpener(opener1.get());
1131 // Make sure the new opener RVH is considered live.
1132 opener1_manager->current_host()->CreateRenderView(base::string16(), -1, -1);
1134 // Use a cross-process navigation in the opener to swap out the old RVH.
1135 EXPECT_FALSE(opener1_manager->GetSwappedOutRenderViewHost(
1136 rvh1->GetSiteInstance()));
1137 opener1->NavigateAndCommit(kUrl2);
1138 EXPECT_TRUE(opener1_manager->GetSwappedOutRenderViewHost(
1139 rvh1->GetSiteInstance()));
1141 // Fake a process crash.
1142 RenderProcessHost::RendererClosedDetails details(
1143 rvh1->GetProcess()->GetHandle(),
1144 base::TERMINATION_STATUS_PROCESS_CRASHED,
1146 NotificationService::current()->Notify(
1147 NOTIFICATION_RENDERER_PROCESS_CLOSED,
1148 Source<RenderProcessHost>(rvh1->GetProcess()),
1149 Details<RenderProcessHost::RendererClosedDetails>(&details));
1150 rvh1->set_render_view_created(false);
1152 // Ensure that the swapped out RenderViewHost has been deleted.
1153 EXPECT_FALSE(opener1_manager->GetSwappedOutRenderViewHost(
1154 rvh1->GetSiteInstance()));
1156 // Reload the initial tab. This should recreate the opener's swapped out RVH
1157 // in the original SiteInstance.
1158 contents()->GetController().Reload(true);
1159 EXPECT_EQ(opener1_manager->GetSwappedOutRenderViewHost(
1160 rvh1->GetSiteInstance())->GetRoutingID(),
1161 test_rvh()->opener_route_id());
1164 // Test that RenderViewHosts created for WebUI navigations are properly
1165 // granted WebUI bindings even if an unprivileged swapped out RenderViewHost
1166 // is in the same process (http://crbug.com/79918).
1167 TEST_F(RenderFrameHostManagerTest, EnableWebUIWithSwappedOutOpener) {
1168 set_should_create_webui(true);
1169 const GURL kSettingsUrl("chrome://chrome/settings");
1170 const GURL kPluginUrl("chrome://plugins");
1172 // Navigate to an initial WebUI URL.
1173 contents()->NavigateAndCommit(kSettingsUrl);
1175 // Ensure the RVH has WebUI bindings.
1176 TestRenderViewHost* rvh1 = test_rvh();
1177 EXPECT_TRUE(rvh1->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1179 // Create a new tab and simulate it being the opener for the main
1180 // tab. It should be in the same SiteInstance.
1181 scoped_ptr<TestWebContents> opener1(
1182 TestWebContents::Create(browser_context(), rvh1->GetSiteInstance()));
1183 RenderFrameHostManager* opener1_manager =
1184 opener1->GetRenderManagerForTesting();
1185 contents()->SetOpener(opener1.get());
1187 // Navigate to a different WebUI URL (different SiteInstance, same
1188 // BrowsingInstance).
1189 contents()->NavigateAndCommit(kPluginUrl);
1190 TestRenderViewHost* rvh2 = test_rvh();
1191 EXPECT_NE(rvh1->GetSiteInstance(), rvh2->GetSiteInstance());
1192 EXPECT_TRUE(rvh1->GetSiteInstance()->IsRelatedSiteInstance(
1193 rvh2->GetSiteInstance()));
1195 // Ensure a swapped out RVH is created in the first opener tab.
1196 TestRenderViewHost* opener1_rvh = static_cast<TestRenderViewHost*>(
1197 opener1_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance()));
1198 EXPECT_TRUE(opener1_manager->IsRVHOnSwappedOutList(opener1_rvh));
1199 EXPECT_TRUE(opener1_rvh->is_swapped_out());
1201 // Ensure the new RVH has WebUI bindings.
1202 EXPECT_TRUE(rvh2->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1205 // Test that we reuse the same guest SiteInstance if we navigate across sites.
1206 TEST_F(RenderFrameHostManagerTest, NoSwapOnGuestNavigations) {
1207 TestNotificationTracker notifications;
1209 GURL guest_url(std::string(kGuestScheme).append("://abc123"));
1210 SiteInstance* instance =
1211 SiteInstance::CreateForURL(browser_context(), guest_url);
1212 scoped_ptr<TestWebContents> web_contents(
1213 TestWebContents::Create(browser_context(), instance));
1216 FrameTree tree(web_contents->GetFrameTree()->root()->navigator(),
1217 web_contents.get(), web_contents.get(),
1218 web_contents.get(), web_contents.get());
1219 RenderFrameHostManager* manager = tree.root()->render_manager();
1221 manager->Init(browser_context(), instance, MSG_ROUTING_NONE,
1224 RenderFrameHostImpl* host;
1226 // 1) The first navigation. --------------------------
1227 const GURL kUrl1("http://www.google.com/");
1228 NavigationEntryImpl entry1(
1229 NULL /* instance */, -1 /* page_id */, kUrl1, Referrer(),
1230 base::string16() /* title */, PAGE_TRANSITION_TYPED,
1231 false /* is_renderer_init */);
1232 host = manager->Navigate(entry1);
1234 // The RenderFrameHost created in Init will be reused.
1235 EXPECT_TRUE(host == manager->current_frame_host());
1236 EXPECT_FALSE(manager->pending_frame_host());
1237 EXPECT_EQ(manager->current_frame_host()->GetSiteInstance(), instance);
1240 manager->DidNavigateMainFrame(host->render_view_host());
1241 // Commit to SiteInstance should be delayed until RenderView commit.
1242 EXPECT_EQ(host, manager->current_frame_host());
1244 EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
1247 // 2) Navigate to a different domain. -------------------------
1248 // Guests stay in the same process on navigation.
1249 const GURL kUrl2("http://www.chromium.org");
1250 NavigationEntryImpl entry2(
1251 NULL /* instance */, -1 /* page_id */, kUrl2,
1252 Referrer(kUrl1, blink::WebReferrerPolicyDefault),
1253 base::string16() /* title */, PAGE_TRANSITION_LINK,
1254 true /* is_renderer_init */);
1255 host = manager->Navigate(entry2);
1257 // The RenderFrameHost created in Init will be reused.
1258 EXPECT_EQ(host, manager->current_frame_host());
1259 EXPECT_FALSE(manager->pending_frame_host());
1262 manager->DidNavigateMainFrame(host->render_view_host());
1263 EXPECT_EQ(host, manager->current_frame_host());
1265 EXPECT_EQ(static_cast<SiteInstanceImpl*>(host->GetSiteInstance()),
1269 // Test that we cancel a pending RVH if we close the tab while it's pending.
1270 // http://crbug.com/294697.
1271 TEST_F(RenderFrameHostManagerTest, NavigateWithEarlyClose) {
1272 TestNotificationTracker notifications;
1274 SiteInstance* instance = SiteInstance::Create(browser_context());
1276 BeforeUnloadFiredWebContentsDelegate delegate;
1277 scoped_ptr<TestWebContents> web_contents(
1278 TestWebContents::Create(browser_context(), instance));
1279 web_contents->SetDelegate(&delegate);
1280 notifications.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
1281 Source<WebContents>(web_contents.get()));
1284 FrameTree tree(web_contents->GetFrameTree()->root()->navigator(),
1285 web_contents.get(), web_contents.get(),
1286 web_contents.get(), web_contents.get());
1287 RenderFrameHostManager* manager = tree.root()->render_manager();
1289 manager->Init(browser_context(), instance, MSG_ROUTING_NONE,
1292 // 1) The first navigation. --------------------------
1293 const GURL kUrl1("http://www.google.com/");
1294 NavigationEntryImpl entry1(NULL /* instance */, -1 /* page_id */, kUrl1,
1295 Referrer(), base::string16() /* title */,
1296 PAGE_TRANSITION_TYPED,
1297 false /* is_renderer_init */);
1298 RenderFrameHostImpl* host = manager->Navigate(entry1);
1300 // The RenderFrameHost created in Init will be reused.
1301 EXPECT_EQ(host, manager->current_frame_host());
1302 EXPECT_FALSE(manager->pending_frame_host());
1304 // We should observe a notification.
1306 notifications.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED));
1307 notifications.Reset();
1310 manager->DidNavigateMainFrame(host->render_view_host());
1312 // Commit to SiteInstance should be delayed until RenderFrame commits.
1313 EXPECT_EQ(host, manager->current_frame_host());
1314 EXPECT_FALSE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
1316 static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->SetSite(kUrl1);
1318 // 2) Cross-site navigate to next site. -------------------------
1319 const GURL kUrl2("http://www.example.com");
1320 NavigationEntryImpl entry2(
1321 NULL /* instance */, -1 /* page_id */, kUrl2, Referrer(),
1322 base::string16() /* title */, PAGE_TRANSITION_TYPED,
1323 false /* is_renderer_init */);
1324 RenderFrameHostImpl* host2 = manager->Navigate(entry2);
1326 // A new RenderFrameHost should be created.
1327 ASSERT_EQ(host2, manager->pending_frame_host());
1328 EXPECT_NE(host2, host);
1330 EXPECT_EQ(host, manager->current_frame_host());
1331 EXPECT_FALSE(manager->current_frame_host()->is_swapped_out());
1332 EXPECT_EQ(host2, manager->pending_frame_host());
1334 // 3) Close the tab. -------------------------
1335 notifications.ListenFor(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
1336 Source<RenderWidgetHost>(host2->render_view_host()));
1337 manager->ShouldClosePage(false, true, base::TimeTicks());
1340 notifications.Check1AndReset(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED));
1341 EXPECT_FALSE(manager->pending_frame_host());
1342 EXPECT_EQ(host, manager->current_frame_host());
1345 } // namespace content