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/cross_site_transferring_request.h"
7 #include "content/browser/frame_host/navigation_controller_impl.h"
8 #include "content/browser/frame_host/navigation_entry_impl.h"
9 #include "content/browser/frame_host/navigator.h"
10 #include "content/browser/frame_host/render_frame_host_manager.h"
11 #include "content/browser/site_instance_impl.h"
12 #include "content/browser/webui/web_ui_controller_factory_registry.h"
13 #include "content/common/frame_messages.h"
14 #include "content/common/view_messages.h"
15 #include "content/public/browser/notification_details.h"
16 #include "content/public/browser/notification_service.h"
17 #include "content/public/browser/notification_source.h"
18 #include "content/public/browser/notification_types.h"
19 #include "content/public/browser/render_process_host.h"
20 #include "content/public/browser/render_widget_host_iterator.h"
21 #include "content/public/browser/web_contents_delegate.h"
22 #include "content/public/browser/web_contents_observer.h"
23 #include "content/public/browser/web_ui_controller.h"
24 #include "content/public/common/bindings_policy.h"
25 #include "content/public/common/javascript_message_type.h"
26 #include "content/public/common/page_transition_types.h"
27 #include "content/public/common/url_constants.h"
28 #include "content/public/common/url_utils.h"
29 #include "content/public/test/mock_render_process_host.h"
30 #include "content/public/test/test_notification_tracker.h"
31 #include "content/test/test_content_browser_client.h"
32 #include "content/test/test_content_client.h"
33 #include "content/test/test_render_view_host.h"
34 #include "content/test/test_web_contents.h"
35 #include "testing/gtest/include/gtest/gtest.h"
40 class RenderFrameHostManagerTestWebUIControllerFactory
41 : public WebUIControllerFactory
{
43 RenderFrameHostManagerTestWebUIControllerFactory()
44 : should_create_webui_(false) {
46 virtual ~RenderFrameHostManagerTestWebUIControllerFactory() {}
48 void set_should_create_webui(bool should_create_webui
) {
49 should_create_webui_
= should_create_webui
;
52 // WebUIFactory implementation.
53 virtual WebUIController
* CreateWebUIControllerForURL(
54 WebUI
* web_ui
, const GURL
& url
) const OVERRIDE
{
55 if (!(should_create_webui_
&& HasWebUIScheme(url
)))
57 return new WebUIController(web_ui
);
60 virtual WebUI::TypeID
GetWebUIType(BrowserContext
* browser_context
,
61 const GURL
& url
) const OVERRIDE
{
62 return WebUI::kNoWebUI
;
65 virtual bool UseWebUIForURL(BrowserContext
* browser_context
,
66 const GURL
& url
) const OVERRIDE
{
67 return HasWebUIScheme(url
);
70 virtual bool UseWebUIBindingsForURL(BrowserContext
* browser_context
,
71 const GURL
& url
) const OVERRIDE
{
72 return HasWebUIScheme(url
);
76 bool should_create_webui_
;
78 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostManagerTestWebUIControllerFactory
);
81 class BeforeUnloadFiredWebContentsDelegate
: public WebContentsDelegate
{
83 BeforeUnloadFiredWebContentsDelegate() {}
84 virtual ~BeforeUnloadFiredWebContentsDelegate() {}
86 virtual void BeforeUnloadFired(WebContents
* web_contents
,
88 bool* proceed_to_fire_unload
) OVERRIDE
{
89 *proceed_to_fire_unload
= proceed
;
93 DISALLOW_COPY_AND_ASSIGN(BeforeUnloadFiredWebContentsDelegate
);
98 class RenderFrameHostManagerTest
99 : public RenderViewHostImplTestHarness
{
101 virtual void SetUp() OVERRIDE
{
102 RenderViewHostImplTestHarness::SetUp();
103 WebUIControllerFactory::RegisterFactory(&factory_
);
106 virtual void TearDown() OVERRIDE
{
107 RenderViewHostImplTestHarness::TearDown();
108 WebUIControllerFactory::UnregisterFactoryForTesting(&factory_
);
111 void set_should_create_webui(bool should_create_webui
) {
112 factory_
.set_should_create_webui(should_create_webui
);
115 void NavigateActiveAndCommit(const GURL
& url
) {
116 // Note: we navigate the active RenderViewHost because previous navigations
117 // won't have committed yet, so NavigateAndCommit does the wrong thing
119 controller().LoadURL(url
, Referrer(), PAGE_TRANSITION_LINK
, std::string());
120 TestRenderViewHost
* old_rvh
= test_rvh();
122 // Simulate the ShouldClose_ACK that is received from the current renderer
123 // for a cross-site navigation.
124 if (old_rvh
!= active_rvh())
125 old_rvh
->SendShouldCloseACK(true);
127 // Commit the navigation with a new page ID.
128 int32 max_page_id
= contents()->GetMaxPageIDForSiteInstance(
129 active_rvh()->GetSiteInstance());
131 // Simulate the SwapOut_ACK that fires if you commit a cross-site
133 if (old_rvh
!= active_rvh())
134 old_rvh
->OnSwappedOut(false);
136 active_test_rvh()->SendNavigate(max_page_id
+ 1, url
);
139 bool ShouldSwapProcesses(RenderFrameHostManager
* manager
,
140 const NavigationEntryImpl
* current_entry
,
141 const NavigationEntryImpl
* new_entry
) const {
142 return manager
->ShouldSwapBrowsingInstancesForNavigation(current_entry
,
146 // Creates a test RenderViewHost that's swapped out.
147 TestRenderViewHost
* CreateSwappedOutRenderViewHost() {
148 const GURL
kChromeURL("chrome://foo");
149 const GURL
kDestUrl("http://www.google.com/");
151 // Navigate our first tab to a chrome url and then to the destination.
152 NavigateActiveAndCommit(kChromeURL
);
153 TestRenderViewHost
* ntp_rvh
= static_cast<TestRenderViewHost
*>(
154 contents()->GetRenderManagerForTesting()->current_host());
156 // Navigate to a cross-site URL.
157 contents()->GetController().LoadURL(
158 kDestUrl
, Referrer(), PAGE_TRANSITION_LINK
, std::string());
159 EXPECT_TRUE(contents()->cross_navigation_pending());
161 // Manually increase the number of active views in the
162 // SiteInstance that ntp_rvh belongs to, to prevent it from being
163 // destroyed when it gets swapped out.
164 static_cast<SiteInstanceImpl
*>(ntp_rvh
->GetSiteInstance())->
165 increment_active_view_count();
167 TestRenderViewHost
* dest_rvh
= static_cast<TestRenderViewHost
*>(
168 contents()->GetRenderManagerForTesting()->pending_render_view_host());
170 EXPECT_NE(ntp_rvh
, dest_rvh
);
172 // BeforeUnload finishes.
173 ntp_rvh
->SendShouldCloseACK(true);
175 dest_rvh
->SendNavigate(101, kDestUrl
);
176 ntp_rvh
->OnSwappedOut(false);
178 EXPECT_TRUE(ntp_rvh
->IsSwappedOut());
183 RenderFrameHostManagerTestWebUIControllerFactory factory_
;
186 // Tests that when you navigate from a chrome:// url to another page, and
187 // then do that same thing in another tab, that the two resulting pages have
188 // different SiteInstances, BrowsingInstances, and RenderProcessHosts. This is
189 // a regression test for bug 9364.
190 TEST_F(RenderFrameHostManagerTest
, NewTabPageProcesses
) {
191 set_should_create_webui(true);
192 const GURL
kChromeUrl("chrome://foo");
193 const GURL
kDestUrl("http://www.google.com/");
195 // Navigate our first tab to the chrome url and then to the destination,
196 // ensuring we grant bindings to the chrome URL.
197 NavigateActiveAndCommit(kChromeUrl
);
198 EXPECT_TRUE(active_rvh()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
199 NavigateActiveAndCommit(kDestUrl
);
201 // Make a second tab.
202 scoped_ptr
<TestWebContents
> contents2(
203 TestWebContents::Create(browser_context(), NULL
));
205 // Load the two URLs in the second tab. Note that the first navigation creates
206 // a RVH that's not pending (since there is no cross-site transition), so
207 // we use the committed one.
208 contents2
->GetController().LoadURL(
209 kChromeUrl
, Referrer(), PAGE_TRANSITION_LINK
, std::string());
210 TestRenderViewHost
* ntp_rvh2
= static_cast<TestRenderViewHost
*>(
211 contents2
->GetRenderManagerForTesting()->current_host());
212 EXPECT_FALSE(contents2
->cross_navigation_pending());
213 ntp_rvh2
->SendNavigate(100, kChromeUrl
);
215 // The second one is the opposite, creating a cross-site transition and
216 // requiring a beforeunload ack.
217 contents2
->GetController().LoadURL(
218 kDestUrl
, Referrer(), PAGE_TRANSITION_LINK
, std::string());
219 EXPECT_TRUE(contents2
->cross_navigation_pending());
220 TestRenderViewHost
* dest_rvh2
= static_cast<TestRenderViewHost
*>(
221 contents2
->GetRenderManagerForTesting()->pending_render_view_host());
222 ASSERT_TRUE(dest_rvh2
);
224 ntp_rvh2
->SendShouldCloseACK(true);
225 ntp_rvh2
->OnSwappedOut(false);
226 dest_rvh2
->SendNavigate(101, kDestUrl
);
228 // The two RVH's should be different in every way.
229 EXPECT_NE(active_rvh()->GetProcess(), dest_rvh2
->GetProcess());
230 EXPECT_NE(active_rvh()->GetSiteInstance(), dest_rvh2
->GetSiteInstance());
231 EXPECT_FALSE(active_rvh()->GetSiteInstance()->IsRelatedSiteInstance(
232 dest_rvh2
->GetSiteInstance()));
234 // Navigate both to the new tab page, and verify that they share a
235 // RenderProcessHost (not a SiteInstance).
236 NavigateActiveAndCommit(kChromeUrl
);
238 contents2
->GetController().LoadURL(
239 kChromeUrl
, Referrer(), PAGE_TRANSITION_LINK
, std::string());
240 dest_rvh2
->SendShouldCloseACK(true);
241 dest_rvh2
->OnSwappedOut(false);
242 static_cast<TestRenderViewHost
*>(contents2
->GetRenderManagerForTesting()->
243 pending_render_view_host())->SendNavigate(102, kChromeUrl
);
245 EXPECT_NE(active_rvh()->GetSiteInstance(),
246 contents2
->GetRenderViewHost()->GetSiteInstance());
247 EXPECT_EQ(active_rvh()->GetSiteInstance()->GetProcess(),
248 contents2
->GetRenderViewHost()->GetSiteInstance()->GetProcess());
251 // Ensure that the browser ignores most IPC messages that arrive from a
252 // RenderViewHost that has been swapped out. We do not want to take
253 // action on requests from a non-active renderer. The main exception is
254 // for synchronous messages, which cannot be ignored without leaving the
255 // renderer in a stuck state. See http://crbug.com/93427.
256 TEST_F(RenderFrameHostManagerTest
, FilterMessagesWhileSwappedOut
) {
257 const GURL
kChromeURL("chrome://foo");
258 const GURL
kDestUrl("http://www.google.com/");
260 // Navigate our first tab to a chrome url and then to the destination.
261 NavigateActiveAndCommit(kChromeURL
);
262 TestRenderViewHost
* ntp_rvh
= static_cast<TestRenderViewHost
*>(
263 contents()->GetRenderManagerForTesting()->current_host());
265 // Send an update title message and make sure it works.
266 const base::string16 ntp_title
= base::ASCIIToUTF16("NTP Title");
267 blink::WebTextDirection direction
= blink::WebTextDirectionLeftToRight
;
268 EXPECT_TRUE(ntp_rvh
->OnMessageReceived(
269 ViewHostMsg_UpdateTitle(rvh()->GetRoutingID(), 0, ntp_title
, direction
)));
270 EXPECT_EQ(ntp_title
, contents()->GetTitle());
272 // Navigate to a cross-site URL.
273 contents()->GetController().LoadURL(
274 kDestUrl
, Referrer(), PAGE_TRANSITION_LINK
, std::string());
275 EXPECT_TRUE(contents()->cross_navigation_pending());
276 TestRenderViewHost
* dest_rvh
= static_cast<TestRenderViewHost
*>(
277 contents()->GetRenderManagerForTesting()->pending_render_view_host());
278 ASSERT_TRUE(dest_rvh
);
279 EXPECT_NE(ntp_rvh
, dest_rvh
);
281 // Create one more view in the same SiteInstance where dest_rvh2
282 // exists so that it doesn't get deleted on navigation to another
284 static_cast<SiteInstanceImpl
*>(ntp_rvh
->GetSiteInstance())->
285 increment_active_view_count();
287 // BeforeUnload finishes.
288 ntp_rvh
->SendShouldCloseACK(true);
290 // Assume SwapOutACK times out, so the dest_rvh proceeds and commits.
291 dest_rvh
->SendNavigate(101, kDestUrl
);
293 // The new RVH should be able to update its title.
294 const base::string16 dest_title
= base::ASCIIToUTF16("Google");
295 EXPECT_TRUE(dest_rvh
->OnMessageReceived(
296 ViewHostMsg_UpdateTitle(rvh()->GetRoutingID(), 101, dest_title
,
298 EXPECT_EQ(dest_title
, contents()->GetTitle());
300 // The old renderer, being slow, now updates the title. It should be filtered
301 // out and not take effect.
302 EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT
, ntp_rvh
->rvh_state());
303 EXPECT_TRUE(ntp_rvh
->OnMessageReceived(
304 ViewHostMsg_UpdateTitle(rvh()->GetRoutingID(), 0, ntp_title
, direction
)));
305 EXPECT_EQ(dest_title
, contents()->GetTitle());
307 // We cannot filter out synchronous IPC messages, because the renderer would
308 // be left waiting for a reply. We pick RunBeforeUnloadConfirm as an example
309 // that can run easily within a unit test, and that needs to receive a reply
310 // without showing an actual dialog.
311 MockRenderProcessHost
* ntp_process_host
=
312 static_cast<MockRenderProcessHost
*>(ntp_rvh
->GetProcess());
313 ntp_process_host
->sink().ClearMessages();
314 const base::string16 msg
= base::ASCIIToUTF16("Message");
316 base::string16 unused
;
317 ViewHostMsg_RunBeforeUnloadConfirm
before_unload_msg(
318 rvh()->GetRoutingID(), kChromeURL
, msg
, false, &result
, &unused
);
319 // Enable pumping for check in BrowserMessageFilter::CheckCanDispatchOnUI.
320 before_unload_msg
.EnableMessagePumping();
321 EXPECT_TRUE(ntp_rvh
->OnMessageReceived(before_unload_msg
));
322 EXPECT_TRUE(ntp_process_host
->sink().GetUniqueMessageMatching(IPC_REPLY_ID
));
324 // Also test RunJavaScriptMessage.
325 ntp_process_host
->sink().ClearMessages();
326 ViewHostMsg_RunJavaScriptMessage
js_msg(
327 rvh()->GetRoutingID(), msg
, msg
, kChromeURL
,
328 JAVASCRIPT_MESSAGE_TYPE_CONFIRM
, &result
, &unused
);
329 js_msg
.EnableMessagePumping();
330 EXPECT_TRUE(ntp_rvh
->OnMessageReceived(js_msg
));
331 EXPECT_TRUE(ntp_process_host
->sink().GetUniqueMessageMatching(IPC_REPLY_ID
));
334 TEST_F(RenderFrameHostManagerTest
, WhiteListSwapCompositorFrame
) {
335 TestRenderViewHost
* swapped_out_rvh
= CreateSwappedOutRenderViewHost();
336 TestRenderWidgetHostView
* swapped_out_rwhv
=
337 static_cast<TestRenderWidgetHostView
*>(swapped_out_rvh
->GetView());
338 EXPECT_FALSE(swapped_out_rwhv
->did_swap_compositor_frame());
340 MockRenderProcessHost
* process_host
=
341 static_cast<MockRenderProcessHost
*>(swapped_out_rvh
->GetProcess());
342 process_host
->sink().ClearMessages();
344 cc::CompositorFrame frame
;
345 ViewHostMsg_SwapCompositorFrame
msg(rvh()->GetRoutingID(), 0, frame
);
347 EXPECT_TRUE(swapped_out_rvh
->OnMessageReceived(msg
));
348 EXPECT_TRUE(swapped_out_rwhv
->did_swap_compositor_frame());
351 TEST_F(RenderFrameHostManagerTest
, WhiteListDidActivateAcceleratedCompositing
) {
352 TestRenderViewHost
* swapped_out_rvh
= CreateSwappedOutRenderViewHost();
354 MockRenderProcessHost
* process_host
=
355 static_cast<MockRenderProcessHost
*>(swapped_out_rvh
->GetProcess());
356 process_host
->sink().ClearMessages();
357 ViewHostMsg_DidActivateAcceleratedCompositing
msg(
358 rvh()->GetRoutingID(), true);
359 EXPECT_TRUE(swapped_out_rvh
->OnMessageReceived(msg
));
360 EXPECT_TRUE(swapped_out_rvh
->is_accelerated_compositing_active());
363 // Test if RenderViewHost::GetRenderWidgetHosts() only returns active
365 TEST_F(RenderFrameHostManagerTest
, GetRenderWidgetHostsReturnsActiveViews
) {
366 TestRenderViewHost
* swapped_out_rvh
= CreateSwappedOutRenderViewHost();
367 EXPECT_TRUE(swapped_out_rvh
->IsSwappedOut());
369 scoped_ptr
<RenderWidgetHostIterator
> widgets(
370 RenderWidgetHost::GetRenderWidgetHosts());
371 // We know that there is the only one active widget. Another view is
372 // now swapped out, so the swapped out view is not included in the
374 RenderWidgetHost
* widget
= widgets
->GetNextHost();
375 EXPECT_FALSE(widgets
->GetNextHost());
376 RenderViewHost
* rvh
= RenderViewHost::From(widget
);
377 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT
,
378 static_cast<RenderViewHostImpl
*>(rvh
)->rvh_state());
381 // Test if RenderViewHost::GetRenderWidgetHosts() returns a subset of
382 // RenderViewHostImpl::GetAllRenderWidgetHosts().
383 // RenderViewHost::GetRenderWidgetHosts() returns only active widgets, but
384 // RenderViewHostImpl::GetAllRenderWidgetHosts() returns everything
385 // including swapped out ones.
386 TEST_F(RenderFrameHostManagerTest
,
387 GetRenderWidgetHostsWithinGetAllRenderWidgetHosts
) {
388 TestRenderViewHost
* swapped_out_rvh
= CreateSwappedOutRenderViewHost();
389 EXPECT_TRUE(swapped_out_rvh
->IsSwappedOut());
391 scoped_ptr
<RenderWidgetHostIterator
> widgets(
392 RenderWidgetHost::GetRenderWidgetHosts());
394 while (RenderWidgetHost
* w
= widgets
->GetNextHost()) {
396 scoped_ptr
<RenderWidgetHostIterator
> all_widgets(
397 RenderWidgetHostImpl::GetAllRenderWidgetHosts());
398 while (RenderWidgetHost
* widget
= all_widgets
->GetNextHost()) {
408 // Test if SiteInstanceImpl::active_view_count() is correctly updated
409 // as views in a SiteInstance get swapped out and in.
410 TEST_F(RenderFrameHostManagerTest
, ActiveViewCountWhileSwappingInandOut
) {
411 const GURL
kUrl1("http://www.google.com/");
412 const GURL
kUrl2("http://www.chromium.org/");
414 // Navigate to an initial URL.
415 contents()->NavigateAndCommit(kUrl1
);
416 TestRenderViewHost
* rvh1
= test_rvh();
418 SiteInstanceImpl
* instance1
=
419 static_cast<SiteInstanceImpl
*>(rvh1
->GetSiteInstance());
420 EXPECT_EQ(instance1
->active_view_count(), 1U);
422 // Create 2 new tabs and simulate them being the opener chain for the main
423 // tab. They should be in the same SiteInstance.
424 scoped_ptr
<TestWebContents
> opener1(
425 TestWebContents::Create(browser_context(), instance1
));
426 contents()->SetOpener(opener1
.get());
428 scoped_ptr
<TestWebContents
> opener2(
429 TestWebContents::Create(browser_context(), instance1
));
430 opener1
->SetOpener(opener2
.get());
432 EXPECT_EQ(instance1
->active_view_count(), 3U);
434 // Navigate to a cross-site URL (different SiteInstance but same
435 // BrowsingInstance).
436 contents()->NavigateAndCommit(kUrl2
);
437 TestRenderViewHost
* rvh2
= test_rvh();
438 SiteInstanceImpl
* instance2
=
439 static_cast<SiteInstanceImpl
*>(rvh2
->GetSiteInstance());
441 // rvh2 is on chromium.org which is different from google.com on
442 // which other tabs are.
443 EXPECT_EQ(instance2
->active_view_count(), 1U);
445 // There are two active views on google.com now.
446 EXPECT_EQ(instance1
->active_view_count(), 2U);
448 // Navigate to the original origin (google.com).
449 contents()->NavigateAndCommit(kUrl1
);
451 EXPECT_EQ(instance1
->active_view_count(), 3U);
454 // This deletes a WebContents when the given RVH is deleted. This is
455 // only for testing whether deleting an RVH does not cause any UaF in
456 // other parts of the system. For now, this class is only used for the
457 // next test cases to detect the bug mentioned at
458 // http://crbug.com/259859.
459 class RenderViewHostDestroyer
: public WebContentsObserver
{
461 RenderViewHostDestroyer(RenderViewHost
* render_view_host
,
462 WebContents
* web_contents
)
463 : WebContentsObserver(WebContents::FromRenderViewHost(render_view_host
)),
464 render_view_host_(render_view_host
),
465 web_contents_(web_contents
) {}
467 virtual void RenderViewDeleted(
468 RenderViewHost
* render_view_host
) OVERRIDE
{
469 if (render_view_host
== render_view_host_
)
470 delete web_contents_
;
474 RenderViewHost
* render_view_host_
;
475 WebContents
* web_contents_
;
477 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestroyer
);
480 // Test if ShutdownRenderViewHostsInSiteInstance() does not touch any
481 // RenderWidget that has been freed while deleting a RenderViewHost in
482 // a previous iteration. This is a regression test for
483 // http://crbug.com/259859.
484 TEST_F(RenderFrameHostManagerTest
,
485 DetectUseAfterFreeInShutdownRenderViewHostsInSiteInstance
) {
486 const GURL
kChromeURL("chrome://newtab");
487 const GURL
kUrl1("http://www.google.com");
488 const GURL
kUrl2("http://www.chromium.org");
490 // Navigate our first tab to a chrome url and then to the destination.
491 NavigateActiveAndCommit(kChromeURL
);
492 TestRenderViewHost
* ntp_rvh
= static_cast<TestRenderViewHost
*>(
493 contents()->GetRenderManagerForTesting()->current_host());
495 // Create one more tab and navigate to kUrl1. web_contents is not
496 // wrapped as scoped_ptr since it intentionally deleted by destroyer
497 // below as part of this test.
498 TestWebContents
* web_contents
=
499 TestWebContents::Create(browser_context(), ntp_rvh
->GetSiteInstance());
500 web_contents
->NavigateAndCommit(kUrl1
);
501 RenderViewHostDestroyer
destroyer(ntp_rvh
, web_contents
);
503 // This causes the first tab to navigate to kUrl2, which destroys
504 // the ntp_rvh in ShutdownRenderViewHostsInSiteInstance(). When
505 // ntp_rvh is destroyed, it also destroys the RVHs in web_contents
506 // too. This can test whether
507 // SiteInstanceImpl::ShutdownRenderViewHostsInSiteInstance() can
508 // touch any object freed in this way or not while iterating through
510 contents()->NavigateAndCommit(kUrl2
);
513 // When there is an error with the specified page, renderer exits view-source
514 // mode. See WebFrameImpl::DidFail(). We check by this test that
515 // EnableViewSourceMode message is sent on every navigation regardless
516 // RenderView is being newly created or reused.
517 TEST_F(RenderFrameHostManagerTest
, AlwaysSendEnableViewSourceMode
) {
518 const GURL
kChromeUrl("chrome://foo");
519 const GURL
kUrl("view-source:http://foo");
521 // We have to navigate to some page at first since without this, the first
522 // navigation will reuse the SiteInstance created by Init(), and the second
523 // one will create a new SiteInstance. Because current_instance and
524 // new_instance will be different, a new RenderViewHost will be created for
525 // the second navigation. We have to avoid this in order to exercise the
526 // target code patch.
527 NavigateActiveAndCommit(kChromeUrl
);
530 controller().LoadURL(
531 kUrl
, Referrer(), PAGE_TRANSITION_TYPED
, std::string());
532 // Simulate response from RenderView for FirePageBeforeUnload.
533 base::TimeTicks now
= base::TimeTicks::Now();
534 test_rvh()->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(
535 rvh()->GetRoutingID(), true, now
, now
));
536 ASSERT_TRUE(pending_rvh()); // New pending RenderViewHost will be created.
537 RenderViewHost
* last_rvh
= pending_rvh();
538 int32 new_id
= contents()->GetMaxPageIDForSiteInstance(
539 active_rvh()->GetSiteInstance()) + 1;
540 pending_test_rvh()->SendNavigate(new_id
, kUrl
);
541 EXPECT_EQ(controller().GetLastCommittedEntryIndex(), 1);
542 ASSERT_TRUE(controller().GetLastCommittedEntry());
543 EXPECT_TRUE(kUrl
== controller().GetLastCommittedEntry()->GetURL());
544 EXPECT_FALSE(controller().GetPendingEntry());
545 // Because we're using TestWebContents and TestRenderViewHost in this
546 // unittest, no one calls WebContentsImpl::RenderViewCreated(). So, we see no
547 // EnableViewSourceMode message, here.
549 // Clear queued messages before load.
550 process()->sink().ClearMessages();
552 controller().LoadURL(
553 kUrl
, Referrer(), PAGE_TRANSITION_TYPED
, std::string());
554 // The same RenderViewHost should be reused.
555 EXPECT_FALSE(pending_rvh());
556 EXPECT_TRUE(last_rvh
== rvh());
557 test_rvh()->SendNavigate(new_id
, kUrl
); // The same page_id returned.
558 EXPECT_EQ(controller().GetLastCommittedEntryIndex(), 1);
559 EXPECT_FALSE(controller().GetPendingEntry());
560 // New message should be sent out to make sure to enter view-source mode.
561 EXPECT_TRUE(process()->sink().GetUniqueMessageMatching(
562 ViewMsg_EnableViewSourceMode::ID
));
565 // Tests the Init function by checking the initial RenderViewHost.
566 TEST_F(RenderFrameHostManagerTest
, Init
) {
567 // Using TestBrowserContext.
568 SiteInstanceImpl
* instance
=
569 static_cast<SiteInstanceImpl
*>(SiteInstance::Create(browser_context()));
570 EXPECT_FALSE(instance
->HasSite());
572 scoped_ptr
<TestWebContents
> web_contents(
573 TestWebContents::Create(browser_context(), instance
));
574 FrameTree
tree(web_contents
->GetFrameTree()->root()->navigator(),
575 web_contents
.get(), web_contents
.get(),
576 web_contents
.get(), web_contents
.get());
577 RenderFrameHostManager
* manager
= tree
.root()->render_manager();
579 manager
->Init(browser_context(), instance
, MSG_ROUTING_NONE
,
582 RenderViewHostImpl
* rvh
= manager
->current_host();
583 RenderFrameHostImpl
* rfh
= manager
->current_frame_host();
586 EXPECT_EQ(rvh
, rfh
->render_view_host());
587 EXPECT_EQ(instance
, rvh
->GetSiteInstance());
588 EXPECT_EQ(web_contents
.get(), rvh
->GetDelegate());
589 EXPECT_EQ(web_contents
.get(), rfh
->delegate());
590 EXPECT_TRUE(manager
->GetRenderWidgetHostView());
591 EXPECT_FALSE(manager
->pending_render_view_host());
594 // Tests the Navigate function. We navigate three sites consecutively and check
595 // how the pending/committed RenderViewHost are modified.
596 TEST_F(RenderFrameHostManagerTest
, Navigate
) {
597 TestNotificationTracker notifications
;
599 SiteInstance
* instance
= SiteInstance::Create(browser_context());
601 scoped_ptr
<TestWebContents
> web_contents(
602 TestWebContents::Create(browser_context(), instance
));
603 notifications
.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED
,
604 Source
<WebContents
>(web_contents
.get()));
607 FrameTree
tree(web_contents
->GetFrameTree()->root()->navigator(),
608 web_contents
.get(), web_contents
.get(),
609 web_contents
.get(), web_contents
.get());
610 RenderFrameHostManager
* manager
= tree
.root()->render_manager();
612 manager
->Init(browser_context(), instance
, MSG_ROUTING_NONE
,
615 RenderFrameHostImpl
* host
;
617 // 1) The first navigation. --------------------------
618 const GURL
kUrl1("http://www.google.com/");
619 NavigationEntryImpl
entry1(
620 NULL
/* instance */, -1 /* page_id */, kUrl1
, Referrer(),
621 base::string16() /* title */, PAGE_TRANSITION_TYPED
,
622 false /* is_renderer_init */);
623 host
= manager
->Navigate(entry1
);
625 // The RenderFrameHost created in Init will be reused.
626 EXPECT_TRUE(host
== manager
->current_frame_host());
627 EXPECT_FALSE(manager
->pending_frame_host());
630 manager
->DidNavigateMainFrame(host
->render_view_host());
631 // Commit to SiteInstance should be delayed until RenderView commit.
632 EXPECT_TRUE(host
== manager
->current_frame_host());
634 EXPECT_FALSE(static_cast<SiteInstanceImpl
*>(host
->GetSiteInstance())->
636 static_cast<SiteInstanceImpl
*>(host
->GetSiteInstance())->SetSite(kUrl1
);
638 // 2) Navigate to next site. -------------------------
639 const GURL
kUrl2("http://www.google.com/foo");
640 NavigationEntryImpl
entry2(
641 NULL
/* instance */, -1 /* page_id */, kUrl2
,
642 Referrer(kUrl1
, blink::WebReferrerPolicyDefault
),
643 base::string16() /* title */, PAGE_TRANSITION_LINK
,
644 true /* is_renderer_init */);
645 host
= manager
->Navigate(entry2
);
647 // The RenderFrameHost created in Init will be reused.
648 EXPECT_TRUE(host
== manager
->current_frame_host());
649 EXPECT_FALSE(manager
->pending_frame_host());
652 manager
->DidNavigateMainFrame(host
->render_view_host());
653 EXPECT_TRUE(host
== manager
->current_frame_host());
655 EXPECT_TRUE(static_cast<SiteInstanceImpl
*>(host
->GetSiteInstance())->
658 // 3) Cross-site navigate to next site. --------------
659 const GURL
kUrl3("http://webkit.org/");
660 NavigationEntryImpl
entry3(
661 NULL
/* instance */, -1 /* page_id */, kUrl3
,
662 Referrer(kUrl2
, blink::WebReferrerPolicyDefault
),
663 base::string16() /* title */, PAGE_TRANSITION_LINK
,
664 false /* is_renderer_init */);
665 host
= manager
->Navigate(entry3
);
667 // A new RenderFrameHost should be created.
668 EXPECT_TRUE(manager
->pending_frame_host());
669 ASSERT_EQ(host
, manager
->pending_frame_host());
671 notifications
.Reset();
674 manager
->DidNavigateMainFrame(manager
->pending_render_view_host());
675 EXPECT_TRUE(host
== manager
->current_frame_host());
677 EXPECT_TRUE(static_cast<SiteInstanceImpl
*>(host
->GetSiteInstance())->
679 // Check the pending RenderFrameHost has been committed.
680 EXPECT_FALSE(manager
->pending_frame_host());
682 // We should observe a notification.
684 notifications
.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED
));
687 // Tests the Navigate function. In this unit test we verify that the Navigate
688 // function can handle a new navigation event before the previous navigation
689 // has been committed. This is also a regression test for
690 // http://crbug.com/104600.
691 TEST_F(RenderFrameHostManagerTest
, NavigateWithEarlyReNavigation
) {
692 TestNotificationTracker notifications
;
694 SiteInstance
* instance
= SiteInstance::Create(browser_context());
696 scoped_ptr
<TestWebContents
> web_contents(
697 TestWebContents::Create(browser_context(), instance
));
698 notifications
.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED
,
699 Source
<WebContents
>(web_contents
.get()));
702 FrameTree
tree(web_contents
->GetFrameTree()->root()->navigator(),
703 web_contents
.get(), web_contents
.get(),
704 web_contents
.get(), web_contents
.get());
705 RenderFrameHostManager
* manager
= tree
.root()->render_manager();
707 manager
->Init(browser_context(), instance
, MSG_ROUTING_NONE
,
710 // 1) The first navigation. --------------------------
711 const GURL
kUrl1("http://www.google.com/");
712 NavigationEntryImpl
entry1(NULL
/* instance */, -1 /* page_id */, kUrl1
,
713 Referrer(), base::string16() /* title */,
714 PAGE_TRANSITION_TYPED
,
715 false /* is_renderer_init */);
716 RenderFrameHostImpl
* host
= manager
->Navigate(entry1
);
718 // The RenderFrameHost created in Init will be reused.
719 EXPECT_TRUE(host
== manager
->current_frame_host());
720 EXPECT_FALSE(manager
->pending_frame_host());
722 // We should observe a notification.
724 notifications
.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED
));
725 notifications
.Reset();
728 manager
->DidNavigateMainFrame(host
->render_view_host());
730 // Commit to SiteInstance should be delayed until RenderView commit.
731 EXPECT_TRUE(host
== manager
->current_frame_host());
733 EXPECT_FALSE(static_cast<SiteInstanceImpl
*>(host
->GetSiteInstance())->
735 static_cast<SiteInstanceImpl
*>(host
->GetSiteInstance())->SetSite(kUrl1
);
737 // 2) Cross-site navigate to next site. -------------------------
738 const GURL
kUrl2("http://www.example.com");
739 NavigationEntryImpl
entry2(
740 NULL
/* instance */, -1 /* page_id */, kUrl2
, Referrer(),
741 base::string16() /* title */, PAGE_TRANSITION_TYPED
,
742 false /* is_renderer_init */);
743 RenderFrameHostImpl
* host2
= manager
->Navigate(entry2
);
744 int host2_process_id
= host2
->GetProcess()->GetID();
746 // A new RenderFrameHost should be created.
747 EXPECT_TRUE(manager
->pending_frame_host());
748 ASSERT_EQ(host2
, manager
->pending_frame_host());
749 EXPECT_NE(host2
, host
);
751 // Check that the navigation is still suspended because the old RVH
752 // is not swapped out, yet.
753 EXPECT_TRUE(host2
->render_view_host()->are_navigations_suspended());
754 MockRenderProcessHost
* test_process_host2
=
755 static_cast<MockRenderProcessHost
*>(host2
->GetProcess());
756 test_process_host2
->sink().ClearMessages();
757 host2
->render_view_host()->NavigateToURL(kUrl2
);
758 EXPECT_FALSE(test_process_host2
->sink().GetUniqueMessageMatching(
759 FrameMsg_Navigate::ID
));
761 // Allow closing the current Render View (precondition for swapping out
762 // the RVH): Simulate response from RenderView for ViewMsg_ShouldClose sent by
763 // FirePageBeforeUnload.
764 TestRenderViewHost
* test_host
=
765 static_cast<TestRenderViewHost
*>(host
->render_view_host());
766 MockRenderProcessHost
* test_process_host
=
767 static_cast<MockRenderProcessHost
*>(test_host
->GetProcess());
768 EXPECT_TRUE(test_process_host
->sink().GetUniqueMessageMatching(
769 ViewMsg_ShouldClose::ID
));
770 test_host
->SendShouldCloseACK(true);
772 // CrossSiteResourceHandler::StartCrossSiteTransition triggers a
773 // call of RenderFrameHostManager::SwapOutOldPage before
774 // RenderFrameHostManager::DidNavigateMainFrame is called.
775 // The RVH is swapped out after receiving the unload ack.
776 manager
->SwapOutOldPage();
777 EXPECT_TRUE(test_process_host
->sink().GetUniqueMessageMatching(
778 ViewMsg_SwapOut::ID
));
779 test_host
->OnSwappedOut(false);
781 EXPECT_EQ(host
, manager
->current_frame_host());
782 EXPECT_FALSE(manager
->current_frame_host()->is_swapped_out());
783 EXPECT_EQ(host2
, manager
->pending_frame_host());
784 // There should be still no navigation messages being sent.
785 EXPECT_FALSE(test_process_host2
->sink().GetUniqueMessageMatching(
786 FrameMsg_Navigate::ID
));
788 // 3) Cross-site navigate to next site before 2) has committed. --------------
789 const GURL
kUrl3("http://webkit.org/");
790 NavigationEntryImpl
entry3(NULL
/* instance */, -1 /* page_id */, kUrl3
,
791 Referrer(), base::string16() /* title */,
792 PAGE_TRANSITION_TYPED
,
793 false /* is_renderer_init */);
794 test_process_host
->sink().ClearMessages();
795 RenderFrameHostImpl
* host3
= manager
->Navigate(entry3
);
797 // A new RenderFrameHost should be created. host2 is now deleted.
798 EXPECT_TRUE(manager
->pending_frame_host());
799 ASSERT_EQ(host3
, manager
->pending_frame_host());
800 EXPECT_NE(host3
, host
);
801 EXPECT_NE(host3
->GetProcess()->GetID(), host2_process_id
);
803 // Navigations in the new RVH should be suspended.
804 EXPECT_TRUE(static_cast<RenderViewHostImpl
*>(
805 host3
->render_view_host())->are_navigations_suspended());
806 EXPECT_EQ(host
, manager
->current_frame_host());
807 EXPECT_FALSE(manager
->current_frame_host()->is_swapped_out());
809 // Simulate a response to the second beforeunload request.
810 EXPECT_TRUE(test_process_host
->sink().GetUniqueMessageMatching(
811 ViewMsg_ShouldClose::ID
));
812 test_host
->SendShouldCloseACK(true);
814 // CrossSiteResourceHandler::StartCrossSiteTransition triggers a
815 // call of RenderFrameHostManager::SwapOutOldPage before
816 // RenderFrameHostManager::DidNavigateMainFrame is called.
817 manager
->SwapOutOldPage();
818 EXPECT_TRUE(test_process_host
->sink().GetUniqueMessageMatching(
819 ViewMsg_SwapOut::ID
));
820 test_host
->OnSwappedOut(false);
823 manager
->DidNavigateMainFrame(host3
->render_view_host());
824 EXPECT_TRUE(host3
== manager
->current_frame_host());
826 EXPECT_TRUE(static_cast<SiteInstanceImpl
*>(host3
->GetSiteInstance())->
828 // Check the pending RenderFrameHost has been committed.
829 EXPECT_FALSE(manager
->pending_frame_host());
831 // We should observe a notification.
833 notifications
.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED
));
836 // Tests WebUI creation.
837 TEST_F(RenderFrameHostManagerTest
, WebUI
) {
838 set_should_create_webui(true);
839 SiteInstance
* instance
= SiteInstance::Create(browser_context());
841 scoped_ptr
<TestWebContents
> web_contents(
842 TestWebContents::Create(browser_context(), instance
));
843 FrameTree
tree(web_contents
->GetFrameTree()->root()->navigator(),
844 web_contents
.get(), web_contents
.get(),
845 web_contents
.get(), web_contents
.get());
846 RenderFrameHostManager
* manager
= tree
.root()->render_manager();
848 manager
->Init(browser_context(), instance
, MSG_ROUTING_NONE
,
850 EXPECT_FALSE(manager
->current_host()->IsRenderViewLive());
852 const GURL
kUrl("chrome://foo");
853 NavigationEntryImpl
entry(NULL
/* instance */, -1 /* page_id */, kUrl
,
854 Referrer(), base::string16() /* title */,
855 PAGE_TRANSITION_TYPED
,
856 false /* is_renderer_init */);
857 RenderFrameHostImpl
* host
= manager
->Navigate(entry
);
859 // We commit the pending RenderFrameHost immediately because the previous
860 // RenderFrameHost was not live. We test a case where it is live in
863 EXPECT_EQ(host
, manager
->current_frame_host());
864 EXPECT_FALSE(manager
->pending_frame_host());
866 // It's important that the site instance get set on the Web UI page as soon
867 // as the navigation starts, rather than lazily after it commits, so we don't
868 // try to re-use the SiteInstance/process for non Web UI things that may
869 // get loaded in between.
870 EXPECT_TRUE(static_cast<SiteInstanceImpl
*>(host
->GetSiteInstance())->
872 EXPECT_EQ(kUrl
, host
->GetSiteInstance()->GetSiteURL());
874 // The Web UI is committed immediately because the RenderViewHost has not been
875 // used yet. UpdateRendererStateForNavigate() took the short cut path.
876 EXPECT_FALSE(manager
->pending_web_ui());
877 EXPECT_TRUE(manager
->web_ui());
880 manager
->DidNavigateMainFrame(host
->render_view_host());
882 host
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
885 // Tests that we can open a WebUI link in a new tab from a WebUI page and still
886 // grant the correct bindings. http://crbug.com/189101.
887 TEST_F(RenderFrameHostManagerTest
, WebUIInNewTab
) {
888 set_should_create_webui(true);
889 SiteInstance
* blank_instance
= SiteInstance::Create(browser_context());
891 // Create a blank tab.
892 scoped_ptr
<TestWebContents
> web_contents1(
893 TestWebContents::Create(browser_context(), blank_instance
));
894 FrameTree
tree1(web_contents1
->GetFrameTree()->root()->navigator(),
895 web_contents1
.get(), web_contents1
.get(),
896 web_contents1
.get(), web_contents1
.get());
897 RenderFrameHostManager
* manager1
= tree1
.root()->render_manager();
899 browser_context(), blank_instance
, MSG_ROUTING_NONE
, MSG_ROUTING_NONE
);
900 // Test the case that new RVH is considered live.
901 manager1
->current_host()->CreateRenderView(base::string16(), -1, -1);
903 // Navigate to a WebUI page.
904 const GURL
kUrl1("chrome://foo");
905 NavigationEntryImpl
entry1(NULL
/* instance */, -1 /* page_id */, kUrl1
,
906 Referrer(), base::string16() /* title */,
907 PAGE_TRANSITION_TYPED
,
908 false /* is_renderer_init */);
909 RenderFrameHostImpl
* host1
= manager1
->Navigate(entry1
);
911 // We should have a pending navigation to the WebUI RenderViewHost.
912 // It should already have bindings.
913 EXPECT_EQ(host1
, manager1
->pending_frame_host());
914 EXPECT_NE(host1
, manager1
->current_frame_host());
916 host1
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
918 // Commit and ensure we still have bindings.
919 manager1
->DidNavigateMainFrame(host1
->render_view_host());
920 SiteInstance
* webui_instance
= host1
->GetSiteInstance();
921 EXPECT_EQ(host1
, manager1
->current_frame_host());
923 host1
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
925 // Now simulate clicking a link that opens in a new tab.
926 scoped_ptr
<TestWebContents
> web_contents2(
927 TestWebContents::Create(browser_context(), webui_instance
));
928 FrameTree
tree2(web_contents2
->GetFrameTree()->root()->navigator(),
929 web_contents2
.get(), web_contents2
.get(),
930 web_contents2
.get(), web_contents2
.get());
931 RenderFrameHostManager
* manager2
= tree2
.root()->render_manager();
933 browser_context(), webui_instance
, MSG_ROUTING_NONE
, MSG_ROUTING_NONE
);
934 // Make sure the new RVH is considered live. This is usually done in
935 // RenderWidgetHost::Init when opening a new tab from a link.
936 manager2
->current_host()->CreateRenderView(base::string16(), -1, -1);
938 const GURL
kUrl2("chrome://foo/bar");
939 NavigationEntryImpl
entry2(NULL
/* instance */, -1 /* page_id */, kUrl2
,
940 Referrer(), base::string16() /* title */,
941 PAGE_TRANSITION_LINK
,
942 true /* is_renderer_init */);
943 RenderFrameHostImpl
* host2
= manager2
->Navigate(entry2
);
945 // No cross-process transition happens because we are already in the right
946 // SiteInstance. We should grant bindings immediately.
947 EXPECT_EQ(host2
, manager2
->current_frame_host());
949 host2
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
951 manager2
->DidNavigateMainFrame(host2
->render_view_host());
954 // Tests that we don't end up in an inconsistent state if a page does a back and
955 // then reload. http://crbug.com/51680
956 TEST_F(RenderFrameHostManagerTest
, PageDoesBackAndReload
) {
957 const GURL
kUrl1("http://www.google.com/");
958 const GURL
kUrl2("http://www.evil-site.com/");
960 // Navigate to a safe site, then an evil site.
961 // This will switch RenderViewHosts. We cannot assert that the first and
962 // second RVHs are different, though, because the first one may be promptly
964 contents()->NavigateAndCommit(kUrl1
);
965 contents()->NavigateAndCommit(kUrl2
);
966 RenderViewHost
* evil_rvh
= contents()->GetRenderViewHost();
968 // Now let's simulate the evil page calling history.back().
969 contents()->OnGoToEntryAtOffset(-1);
970 // We should have a new pending RVH.
971 // Note that in this case, the navigation has not committed, so evil_rvh will
972 // not be deleted yet.
973 EXPECT_NE(evil_rvh
, contents()->GetRenderManagerForTesting()->
974 pending_render_view_host());
976 // Before that RVH has committed, the evil page reloads itself.
977 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
980 params
.transition
= PAGE_TRANSITION_CLIENT_REDIRECT
;
981 params
.should_update_history
= false;
982 params
.gesture
= NavigationGestureAuto
;
983 params
.was_within_same_page
= false;
984 params
.is_post
= false;
985 params
.page_state
= PageState::CreateFromURL(kUrl2
);
987 RenderViewHostImpl
* rvh
= static_cast<RenderViewHostImpl
*>(evil_rvh
);
988 RenderFrameHostImpl
* rfh
= RenderFrameHostImpl::FromID(
989 rvh
->GetProcess()->GetID(), rvh
->main_frame_routing_id());
990 contents()->GetFrameTree()->root()->navigator()->DidNavigate(rfh
, params
);
992 // That should have cancelled the pending RVH, and the evil RVH should be the
994 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
995 pending_render_view_host() == NULL
);
996 EXPECT_EQ(evil_rvh
, contents()->GetRenderManagerForTesting()->current_host());
998 // Also we should not have a pending navigation entry.
999 EXPECT_TRUE(contents()->GetController().GetPendingEntry() == NULL
);
1000 NavigationEntry
* entry
= contents()->GetController().GetVisibleEntry();
1001 ASSERT_TRUE(entry
!= NULL
);
1002 EXPECT_EQ(kUrl2
, entry
->GetURL());
1005 // Ensure that we can go back and forward even if a SwapOut ACK isn't received.
1006 // See http://crbug.com/93427.
1007 TEST_F(RenderFrameHostManagerTest
, NavigateAfterMissingSwapOutACK
) {
1008 const GURL
kUrl1("http://www.google.com/");
1009 const GURL
kUrl2("http://www.chromium.org/");
1011 // Navigate to two pages.
1012 contents()->NavigateAndCommit(kUrl1
);
1013 TestRenderViewHost
* rvh1
= test_rvh();
1015 // Keep active_view_count nonzero so that no swapped out views in
1016 // this SiteInstance get forcefully deleted.
1017 static_cast<SiteInstanceImpl
*>(rvh1
->GetSiteInstance())->
1018 increment_active_view_count();
1020 contents()->NavigateAndCommit(kUrl2
);
1021 TestRenderViewHost
* rvh2
= test_rvh();
1022 static_cast<SiteInstanceImpl
*>(rvh2
->GetSiteInstance())->
1023 increment_active_view_count();
1025 // Now go back, but suppose the SwapOut_ACK isn't received. This shouldn't
1026 // happen, but we have seen it when going back quickly across many entries
1027 // (http://crbug.com/93427).
1028 contents()->GetController().GoBack();
1029 EXPECT_TRUE(rvh2
->is_waiting_for_beforeunload_ack());
1030 contents()->ProceedWithCrossSiteNavigation();
1031 EXPECT_FALSE(rvh2
->is_waiting_for_beforeunload_ack());
1033 EXPECT_TRUE(rvh2
->IsWaitingForUnloadACK());
1035 // The back navigation commits.
1036 const NavigationEntry
* entry1
= contents()->GetController().GetPendingEntry();
1037 rvh1
->SendNavigate(entry1
->GetPageID(), entry1
->GetURL());
1038 EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT
, rvh2
->rvh_state());
1040 // We should be able to navigate forward.
1041 contents()->GetController().GoForward();
1042 contents()->ProceedWithCrossSiteNavigation();
1043 const NavigationEntry
* entry2
= contents()->GetController().GetPendingEntry();
1044 rvh2
->SendNavigate(entry2
->GetPageID(), entry2
->GetURL());
1045 EXPECT_EQ(rvh2
, rvh());
1046 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT
, rvh2
->rvh_state());
1047 EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT
, rvh1
->rvh_state());
1048 rvh1
->OnSwappedOut(false);
1049 EXPECT_TRUE(rvh1
->IsSwappedOut());
1052 // Test that we create swapped out RVHs for the opener chain when navigating an
1053 // opened tab cross-process. This allows us to support certain cross-process
1054 // JavaScript calls (http://crbug.com/99202).
1055 TEST_F(RenderFrameHostManagerTest
, CreateSwappedOutOpenerRVHs
) {
1056 const GURL
kUrl1("http://www.google.com/");
1057 const GURL
kUrl2("http://www.chromium.org/");
1058 const GURL
kChromeUrl("chrome://foo");
1060 // Navigate to an initial URL.
1061 contents()->NavigateAndCommit(kUrl1
);
1062 RenderFrameHostManager
* manager
= contents()->GetRenderManagerForTesting();
1063 TestRenderViewHost
* rvh1
= test_rvh();
1065 // Create 2 new tabs and simulate them being the opener chain for the main
1066 // tab. They should be in the same SiteInstance.
1067 scoped_ptr
<TestWebContents
> opener1(
1068 TestWebContents::Create(browser_context(), rvh1
->GetSiteInstance()));
1069 RenderFrameHostManager
* opener1_manager
=
1070 opener1
->GetRenderManagerForTesting();
1071 contents()->SetOpener(opener1
.get());
1073 scoped_ptr
<TestWebContents
> opener2(
1074 TestWebContents::Create(browser_context(), rvh1
->GetSiteInstance()));
1075 RenderFrameHostManager
* opener2_manager
=
1076 opener2
->GetRenderManagerForTesting();
1077 opener1
->SetOpener(opener2
.get());
1079 // Navigate to a cross-site URL (different SiteInstance but same
1080 // BrowsingInstance).
1081 contents()->NavigateAndCommit(kUrl2
);
1082 TestRenderViewHost
* rvh2
= test_rvh();
1083 EXPECT_NE(rvh1
->GetSiteInstance(), rvh2
->GetSiteInstance());
1084 EXPECT_TRUE(rvh1
->GetSiteInstance()->IsRelatedSiteInstance(
1085 rvh2
->GetSiteInstance()));
1087 // Ensure rvh1 is placed on swapped out list of the current tab.
1088 EXPECT_TRUE(manager
->IsRVHOnSwappedOutList(rvh1
));
1090 manager
->GetSwappedOutRenderViewHost(rvh1
->GetSiteInstance()));
1092 // Ensure a swapped out RVH is created in the first opener tab.
1093 TestRenderViewHost
* opener1_rvh
= static_cast<TestRenderViewHost
*>(
1094 opener1_manager
->GetSwappedOutRenderViewHost(rvh2
->GetSiteInstance()));
1095 EXPECT_TRUE(opener1_manager
->IsRVHOnSwappedOutList(opener1_rvh
));
1096 EXPECT_TRUE(opener1_rvh
->IsSwappedOut());
1098 // Ensure a swapped out RVH is created in the second opener tab.
1099 TestRenderViewHost
* opener2_rvh
= static_cast<TestRenderViewHost
*>(
1100 opener2_manager
->GetSwappedOutRenderViewHost(rvh2
->GetSiteInstance()));
1101 EXPECT_TRUE(opener2_manager
->IsRVHOnSwappedOutList(opener2_rvh
));
1102 EXPECT_TRUE(opener2_rvh
->IsSwappedOut());
1104 // Navigate to a cross-BrowsingInstance URL.
1105 contents()->NavigateAndCommit(kChromeUrl
);
1106 TestRenderViewHost
* rvh3
= test_rvh();
1107 EXPECT_NE(rvh1
->GetSiteInstance(), rvh3
->GetSiteInstance());
1108 EXPECT_FALSE(rvh1
->GetSiteInstance()->IsRelatedSiteInstance(
1109 rvh3
->GetSiteInstance()));
1111 // No scripting is allowed across BrowsingInstances, so we should not create
1112 // swapped out RVHs for the opener chain in this case.
1113 EXPECT_FALSE(opener1_manager
->GetSwappedOutRenderViewHost(
1114 rvh3
->GetSiteInstance()));
1115 EXPECT_FALSE(opener2_manager
->GetSwappedOutRenderViewHost(
1116 rvh3
->GetSiteInstance()));
1119 // Test that we clean up swapped out RenderViewHosts when a process hosting
1120 // those associated RenderViews crashes. http://crbug.com/258993
1121 TEST_F(RenderFrameHostManagerTest
, CleanUpSwappedOutRVHOnProcessCrash
) {
1122 const GURL
kUrl1("http://www.google.com/");
1123 const GURL
kUrl2("http://www.chromium.org/");
1125 // Navigate to an initial URL.
1126 contents()->NavigateAndCommit(kUrl1
);
1127 TestRenderViewHost
* rvh1
= test_rvh();
1129 // Create a new tab as an opener for the main tab.
1130 scoped_ptr
<TestWebContents
> opener1(
1131 TestWebContents::Create(browser_context(), rvh1
->GetSiteInstance()));
1132 RenderFrameHostManager
* opener1_manager
=
1133 opener1
->GetRenderManagerForTesting();
1134 contents()->SetOpener(opener1
.get());
1136 // Make sure the new opener RVH is considered live.
1137 opener1_manager
->current_host()->CreateRenderView(base::string16(), -1, -1);
1139 // Use a cross-process navigation in the opener to swap out the old RVH.
1140 EXPECT_FALSE(opener1_manager
->GetSwappedOutRenderViewHost(
1141 rvh1
->GetSiteInstance()));
1142 opener1
->NavigateAndCommit(kUrl2
);
1143 EXPECT_TRUE(opener1_manager
->GetSwappedOutRenderViewHost(
1144 rvh1
->GetSiteInstance()));
1146 // Fake a process crash.
1147 RenderProcessHost::RendererClosedDetails
details(
1148 rvh1
->GetProcess()->GetHandle(),
1149 base::TERMINATION_STATUS_PROCESS_CRASHED
,
1151 NotificationService::current()->Notify(
1152 NOTIFICATION_RENDERER_PROCESS_CLOSED
,
1153 Source
<RenderProcessHost
>(rvh1
->GetProcess()),
1154 Details
<RenderProcessHost::RendererClosedDetails
>(&details
));
1155 rvh1
->set_render_view_created(false);
1157 // Ensure that the swapped out RenderViewHost has been deleted.
1158 EXPECT_FALSE(opener1_manager
->GetSwappedOutRenderViewHost(
1159 rvh1
->GetSiteInstance()));
1161 // Reload the initial tab. This should recreate the opener's swapped out RVH
1162 // in the original SiteInstance.
1163 contents()->GetController().Reload(true);
1164 EXPECT_EQ(opener1_manager
->GetSwappedOutRenderViewHost(
1165 rvh1
->GetSiteInstance())->GetRoutingID(),
1166 test_rvh()->opener_route_id());
1169 // Test that RenderViewHosts created for WebUI navigations are properly
1170 // granted WebUI bindings even if an unprivileged swapped out RenderViewHost
1171 // is in the same process (http://crbug.com/79918).
1172 TEST_F(RenderFrameHostManagerTest
, EnableWebUIWithSwappedOutOpener
) {
1173 set_should_create_webui(true);
1174 const GURL
kSettingsUrl("chrome://chrome/settings");
1175 const GURL
kPluginUrl("chrome://plugins");
1177 // Navigate to an initial WebUI URL.
1178 contents()->NavigateAndCommit(kSettingsUrl
);
1180 // Ensure the RVH has WebUI bindings.
1181 TestRenderViewHost
* rvh1
= test_rvh();
1182 EXPECT_TRUE(rvh1
->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1184 // Create a new tab and simulate it being the opener for the main
1185 // tab. It should be in the same SiteInstance.
1186 scoped_ptr
<TestWebContents
> opener1(
1187 TestWebContents::Create(browser_context(), rvh1
->GetSiteInstance()));
1188 RenderFrameHostManager
* opener1_manager
=
1189 opener1
->GetRenderManagerForTesting();
1190 contents()->SetOpener(opener1
.get());
1192 // Navigate to a different WebUI URL (different SiteInstance, same
1193 // BrowsingInstance).
1194 contents()->NavigateAndCommit(kPluginUrl
);
1195 TestRenderViewHost
* rvh2
= test_rvh();
1196 EXPECT_NE(rvh1
->GetSiteInstance(), rvh2
->GetSiteInstance());
1197 EXPECT_TRUE(rvh1
->GetSiteInstance()->IsRelatedSiteInstance(
1198 rvh2
->GetSiteInstance()));
1200 // Ensure a swapped out RVH is created in the first opener tab.
1201 TestRenderViewHost
* opener1_rvh
= static_cast<TestRenderViewHost
*>(
1202 opener1_manager
->GetSwappedOutRenderViewHost(rvh2
->GetSiteInstance()));
1203 EXPECT_TRUE(opener1_manager
->IsRVHOnSwappedOutList(opener1_rvh
));
1204 EXPECT_TRUE(opener1_rvh
->IsSwappedOut());
1206 // Ensure the new RVH has WebUI bindings.
1207 EXPECT_TRUE(rvh2
->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1210 // Test that we reuse the same guest SiteInstance if we navigate across sites.
1211 TEST_F(RenderFrameHostManagerTest
, NoSwapOnGuestNavigations
) {
1212 TestNotificationTracker notifications
;
1214 GURL
guest_url(std::string(kGuestScheme
).append("://abc123"));
1215 SiteInstance
* instance
=
1216 SiteInstance::CreateForURL(browser_context(), guest_url
);
1217 scoped_ptr
<TestWebContents
> web_contents(
1218 TestWebContents::Create(browser_context(), instance
));
1221 FrameTree
tree(web_contents
->GetFrameTree()->root()->navigator(),
1222 web_contents
.get(), web_contents
.get(),
1223 web_contents
.get(), web_contents
.get());
1224 RenderFrameHostManager
* manager
= tree
.root()->render_manager();
1226 manager
->Init(browser_context(), instance
, MSG_ROUTING_NONE
,
1229 RenderFrameHostImpl
* host
;
1231 // 1) The first navigation. --------------------------
1232 const GURL
kUrl1("http://www.google.com/");
1233 NavigationEntryImpl
entry1(
1234 NULL
/* instance */, -1 /* page_id */, kUrl1
, Referrer(),
1235 base::string16() /* title */, PAGE_TRANSITION_TYPED
,
1236 false /* is_renderer_init */);
1237 host
= manager
->Navigate(entry1
);
1239 // The RenderFrameHost created in Init will be reused.
1240 EXPECT_TRUE(host
== manager
->current_frame_host());
1241 EXPECT_FALSE(manager
->pending_frame_host());
1242 EXPECT_EQ(manager
->current_frame_host()->GetSiteInstance(), instance
);
1245 manager
->DidNavigateMainFrame(host
->render_view_host());
1246 // Commit to SiteInstance should be delayed until RenderView commit.
1247 EXPECT_EQ(host
, manager
->current_frame_host());
1249 EXPECT_TRUE(static_cast<SiteInstanceImpl
*>(host
->GetSiteInstance())->
1252 // 2) Navigate to a different domain. -------------------------
1253 // Guests stay in the same process on navigation.
1254 const GURL
kUrl2("http://www.chromium.org");
1255 NavigationEntryImpl
entry2(
1256 NULL
/* instance */, -1 /* page_id */, kUrl2
,
1257 Referrer(kUrl1
, blink::WebReferrerPolicyDefault
),
1258 base::string16() /* title */, PAGE_TRANSITION_LINK
,
1259 true /* is_renderer_init */);
1260 host
= manager
->Navigate(entry2
);
1262 // The RenderFrameHost created in Init will be reused.
1263 EXPECT_EQ(host
, manager
->current_frame_host());
1264 EXPECT_FALSE(manager
->pending_frame_host());
1267 manager
->DidNavigateMainFrame(host
->render_view_host());
1268 EXPECT_EQ(host
, manager
->current_frame_host());
1270 EXPECT_EQ(static_cast<SiteInstanceImpl
*>(host
->GetSiteInstance()),
1274 // Test that we cancel a pending RVH if we close the tab while it's pending.
1275 // http://crbug.com/294697.
1276 TEST_F(RenderFrameHostManagerTest
, NavigateWithEarlyClose
) {
1277 TestNotificationTracker notifications
;
1279 SiteInstance
* instance
= SiteInstance::Create(browser_context());
1281 BeforeUnloadFiredWebContentsDelegate delegate
;
1282 scoped_ptr
<TestWebContents
> web_contents(
1283 TestWebContents::Create(browser_context(), instance
));
1284 web_contents
->SetDelegate(&delegate
);
1285 notifications
.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED
,
1286 Source
<WebContents
>(web_contents
.get()));
1289 FrameTree
tree(web_contents
->GetFrameTree()->root()->navigator(),
1290 web_contents
.get(), web_contents
.get(),
1291 web_contents
.get(), web_contents
.get());
1292 RenderFrameHostManager
* manager
= tree
.root()->render_manager();
1294 manager
->Init(browser_context(), instance
, MSG_ROUTING_NONE
,
1297 // 1) The first navigation. --------------------------
1298 const GURL
kUrl1("http://www.google.com/");
1299 NavigationEntryImpl
entry1(NULL
/* instance */, -1 /* page_id */, kUrl1
,
1300 Referrer(), base::string16() /* title */,
1301 PAGE_TRANSITION_TYPED
,
1302 false /* is_renderer_init */);
1303 RenderFrameHostImpl
* host
= manager
->Navigate(entry1
);
1305 // The RenderFrameHost created in Init will be reused.
1306 EXPECT_EQ(host
, manager
->current_frame_host());
1307 EXPECT_FALSE(manager
->pending_frame_host());
1309 // We should observe a notification.
1311 notifications
.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED
));
1312 notifications
.Reset();
1315 manager
->DidNavigateMainFrame(host
->render_view_host());
1317 // Commit to SiteInstance should be delayed until RenderFrame commits.
1318 EXPECT_EQ(host
, manager
->current_frame_host());
1319 EXPECT_FALSE(static_cast<SiteInstanceImpl
*>(host
->GetSiteInstance())->
1321 static_cast<SiteInstanceImpl
*>(host
->GetSiteInstance())->SetSite(kUrl1
);
1323 // 2) Cross-site navigate to next site. -------------------------
1324 const GURL
kUrl2("http://www.example.com");
1325 NavigationEntryImpl
entry2(
1326 NULL
/* instance */, -1 /* page_id */, kUrl2
, Referrer(),
1327 base::string16() /* title */, PAGE_TRANSITION_TYPED
,
1328 false /* is_renderer_init */);
1329 RenderFrameHostImpl
* host2
= manager
->Navigate(entry2
);
1331 // A new RenderFrameHost should be created.
1332 ASSERT_EQ(host2
, manager
->pending_frame_host());
1333 EXPECT_NE(host2
, host
);
1335 EXPECT_EQ(host
, manager
->current_frame_host());
1336 EXPECT_FALSE(manager
->current_frame_host()->is_swapped_out());
1337 EXPECT_EQ(host2
, manager
->pending_frame_host());
1339 // 3) Close the tab. -------------------------
1340 notifications
.ListenFor(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED
,
1341 Source
<RenderWidgetHost
>(host2
->render_view_host()));
1342 manager
->ShouldClosePage(false, true, base::TimeTicks());
1345 notifications
.Check1AndReset(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED
));
1346 EXPECT_FALSE(manager
->pending_frame_host());
1347 EXPECT_EQ(host
, manager
->current_frame_host());
1350 // This checks that the given RVH has been properly deleted.
1351 class RenderViewHostDestructionObserver
: public WebContentsObserver
{
1353 RenderViewHostDestructionObserver(RenderViewHost
* render_view_host
)
1354 : WebContentsObserver(WebContents::FromRenderViewHost(render_view_host
)),
1355 render_view_host_(render_view_host
),
1356 rvh_deleted_(false) {}
1358 bool rvh_deleted() { return rvh_deleted_
; }
1360 virtual void RenderViewDeleted(RenderViewHost
* render_view_host
) OVERRIDE
{
1361 if (render_view_host
== render_view_host_
)
1362 rvh_deleted_
= true;
1366 RenderViewHost
* render_view_host_
;
1369 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestructionObserver
);
1372 // Tests that the RenderViewHost is properly deleted when the SwapOutACK is
1373 // received before the new page commits.
1374 TEST_F(RenderFrameHostManagerTest
,
1375 SwapOutACKBeforeNewPageCommitsLeadsToDeletion
) {
1376 const GURL
kUrl1("http://www.google.com/");
1377 const GURL
kUrl2("http://www.chromium.org/");
1379 // Navigate to the first page.
1380 contents()->NavigateAndCommit(kUrl1
);
1381 TestRenderViewHost
* rvh1
= test_rvh();
1382 RenderViewHostDestructionObserver
destruction_observer(rvh1
);
1383 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT
, rvh1
->rvh_state());
1385 // Navigate to new site, simulating onbeforeunload approval.
1386 controller().LoadURL(kUrl2
, Referrer(), PAGE_TRANSITION_LINK
, std::string());
1387 base::TimeTicks now
= base::TimeTicks::Now();
1388 rvh1
->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now
, now
));
1389 EXPECT_TRUE(contents()->cross_navigation_pending());
1390 TestRenderViewHost
* rvh2
=
1391 static_cast<TestRenderViewHost
*>(contents()->GetPendingRenderViewHost());
1393 // Simulate rvh2's response, which leads to an unload request being sent to
1395 std::vector
<GURL
> url_chain
;
1396 url_chain
.push_back(GURL());
1397 contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
1398 contents()->GetRenderManagerForTesting()->pending_frame_host(),
1399 GlobalRequestID(0, 0), scoped_ptr
<CrossSiteTransferringRequest
>(),
1400 url_chain
, Referrer(), PAGE_TRANSITION_TYPED
, false);
1401 EXPECT_TRUE(contents()->cross_navigation_pending());
1402 EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK
,
1405 // Simulate the swap out ack.
1406 rvh1
->OnSwappedOut(false);
1407 EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT
, rvh1
->rvh_state());
1409 // The new page commits.
1410 contents()->TestDidNavigate(rvh2
, 1, kUrl2
, PAGE_TRANSITION_TYPED
);
1411 EXPECT_FALSE(contents()->cross_navigation_pending());
1412 EXPECT_EQ(rvh2
, rvh());
1413 EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL
);
1414 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT
, rvh2
->rvh_state());
1416 // rvh1 should have been deleted.
1417 EXPECT_TRUE(destruction_observer
.rvh_deleted());
1421 // Tests that the RenderViewHost is properly swapped out when the SwapOutACK is
1422 // received before the new page commits.
1423 TEST_F(RenderFrameHostManagerTest
,
1424 SwapOutACKBeforeNewPageCommitsLeadsToSwapOut
) {
1425 const GURL
kUrl1("http://www.google.com/");
1426 const GURL
kUrl2("http://www.chromium.org/");
1428 // Navigate to the first page.
1429 contents()->NavigateAndCommit(kUrl1
);
1430 TestRenderViewHost
* rvh1
= test_rvh();
1431 RenderViewHostDestructionObserver
destruction_observer(rvh1
);
1432 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT
, rvh1
->rvh_state());
1434 // Increment the number of active views in SiteInstanceImpl so that rvh2 is
1435 // not deleted on swap out.
1436 static_cast<SiteInstanceImpl
*>(
1437 rvh1
->GetSiteInstance())->increment_active_view_count();
1439 // Navigate to new site, simulating onbeforeunload approval.
1440 controller().LoadURL(kUrl2
, Referrer(), PAGE_TRANSITION_LINK
, std::string());
1441 base::TimeTicks now
= base::TimeTicks::Now();
1442 rvh1
->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now
, now
));
1443 EXPECT_TRUE(contents()->cross_navigation_pending());
1444 TestRenderViewHost
* rvh2
=
1445 static_cast<TestRenderViewHost
*>(contents()->GetPendingRenderViewHost());
1447 // Simulate rvh2's response, which leads to an unload request being sent to
1449 std::vector
<GURL
> url_chain
;
1450 url_chain
.push_back(GURL());
1451 contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
1452 contents()->GetRenderManagerForTesting()->pending_frame_host(),
1453 GlobalRequestID(0, 0), scoped_ptr
<CrossSiteTransferringRequest
>(),
1454 url_chain
, Referrer(), PAGE_TRANSITION_TYPED
, false);
1455 EXPECT_TRUE(contents()->cross_navigation_pending());
1456 EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK
,
1459 // Simulate the swap out ack.
1460 rvh1
->OnSwappedOut(false);
1461 EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT
, rvh1
->rvh_state());
1463 // The new page commits.
1464 contents()->TestDidNavigate(rvh2
, 1, kUrl2
, PAGE_TRANSITION_TYPED
);
1465 EXPECT_FALSE(contents()->cross_navigation_pending());
1466 EXPECT_EQ(rvh2
, rvh());
1467 EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL
);
1468 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT
, rvh2
->rvh_state());
1470 // rvh1 should be swapped out.
1471 EXPECT_FALSE(destruction_observer
.rvh_deleted());
1472 EXPECT_TRUE(rvh1
->IsSwappedOut());
1475 // Tests that the RenderViewHost is properly deleted when the new
1476 // page commits before the swap out ack is received.
1477 TEST_F(RenderFrameHostManagerTest
,
1478 NewPageCommitsBeforeSwapOutACKLeadsToDeletion
) {
1479 const GURL
kUrl1("http://www.google.com/");
1480 const GURL
kUrl2("http://www.chromium.org/");
1482 // Navigate to the first page.
1483 contents()->NavigateAndCommit(kUrl1
);
1484 TestRenderViewHost
* rvh1
= test_rvh();
1485 RenderViewHostDestructionObserver
destruction_observer(rvh1
);
1486 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT
, rvh1
->rvh_state());
1488 // Navigate to new site, simulating onbeforeunload approval.
1489 controller().LoadURL(kUrl2
, Referrer(), PAGE_TRANSITION_LINK
, std::string());
1490 base::TimeTicks now
= base::TimeTicks::Now();
1491 rvh1
->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now
, now
));
1492 EXPECT_TRUE(contents()->cross_navigation_pending());
1493 TestRenderViewHost
* rvh2
=
1494 static_cast<TestRenderViewHost
*>(contents()->GetPendingRenderViewHost());
1496 // Simulate rvh2's response, which leads to an unload request being sent to
1498 std::vector
<GURL
> url_chain
;
1499 url_chain
.push_back(GURL());
1500 contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
1501 contents()->GetRenderManagerForTesting()->pending_frame_host(),
1502 GlobalRequestID(0, 0), scoped_ptr
<CrossSiteTransferringRequest
>(),
1503 url_chain
, Referrer(), PAGE_TRANSITION_TYPED
, false);
1504 EXPECT_TRUE(contents()->cross_navigation_pending());
1505 EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK
,
1508 // The new page commits.
1509 contents()->TestDidNavigate(rvh2
, 1, kUrl2
, PAGE_TRANSITION_TYPED
);
1510 EXPECT_FALSE(contents()->cross_navigation_pending());
1511 EXPECT_EQ(rvh2
, rvh());
1512 EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL
);
1513 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT
, rvh2
->rvh_state());
1514 EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SHUTDOWN
, rvh1
->rvh_state());
1516 // Simulate the swap out ack.
1517 rvh1
->OnSwappedOut(false);
1519 // rvh1 should have been deleted.
1520 EXPECT_TRUE(destruction_observer
.rvh_deleted());
1524 // Tests that the RenderViewHost is properly swapped out when the new page
1525 // commits before the swap out ack is received.
1526 TEST_F(RenderFrameHostManagerTest
,
1527 NewPageCommitsBeforeSwapOutACKLeadsToSwapOut
) {
1528 const GURL
kUrl1("http://www.google.com/");
1529 const GURL
kUrl2("http://www.chromium.org/");
1531 // Navigate to the first page.
1532 contents()->NavigateAndCommit(kUrl1
);
1533 TestRenderViewHost
* rvh1
= test_rvh();
1534 RenderViewHostDestructionObserver
destruction_observer(rvh1
);
1535 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT
, rvh1
->rvh_state());
1537 // Increment the number of active views in SiteInstanceImpl so that rvh1 is
1538 // not deleted on swap out.
1539 static_cast<SiteInstanceImpl
*>(
1540 rvh1
->GetSiteInstance())->increment_active_view_count();
1542 // Navigate to new site, simulating onbeforeunload approval.
1543 controller().LoadURL(kUrl2
, Referrer(), PAGE_TRANSITION_LINK
, std::string());
1544 base::TimeTicks now
= base::TimeTicks::Now();
1545 rvh1
->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now
, now
));
1546 EXPECT_TRUE(contents()->cross_navigation_pending());
1547 TestRenderViewHost
* rvh2
=
1548 static_cast<TestRenderViewHost
*>(contents()->GetPendingRenderViewHost());
1550 // Simulate rvh2's response, which leads to an unload request being sent to
1552 std::vector
<GURL
> url_chain
;
1553 url_chain
.push_back(GURL());
1554 contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
1555 contents()->GetRenderManagerForTesting()->pending_frame_host(),
1556 GlobalRequestID(0, 0), scoped_ptr
<CrossSiteTransferringRequest
>(),
1557 url_chain
, Referrer(), PAGE_TRANSITION_TYPED
, false);
1558 EXPECT_TRUE(contents()->cross_navigation_pending());
1559 EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK
,
1562 // The new page commits.
1563 contents()->TestDidNavigate(rvh2
, 1, kUrl2
, PAGE_TRANSITION_TYPED
);
1564 EXPECT_FALSE(contents()->cross_navigation_pending());
1565 EXPECT_EQ(rvh2
, rvh());
1566 EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL
);
1567 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT
, rvh2
->rvh_state());
1568 EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT
, rvh1
->rvh_state());
1570 // Simulate the swap out ack.
1571 rvh1
->OnSwappedOut(false);
1573 // rvh1 should be swapped out.
1574 EXPECT_FALSE(destruction_observer
.rvh_deleted());
1575 EXPECT_TRUE(rvh1
->IsSwappedOut());
1578 } // namespace content