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/command_line.h"
6 #include "base/files/file_path.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "base/test/histogram_tester.h"
9 #include "base/time/time.h"
10 #include "content/browser/frame_host/cross_site_transferring_request.h"
11 #include "content/browser/frame_host/navigation_controller_impl.h"
12 #include "content/browser/frame_host/navigation_entry_impl.h"
13 #include "content/browser/frame_host/navigator.h"
14 #include "content/browser/frame_host/render_frame_host_manager.h"
15 #include "content/browser/frame_host/render_frame_proxy_host.h"
16 #include "content/browser/site_instance_impl.h"
17 #include "content/browser/webui/web_ui_controller_factory_registry.h"
18 #include "content/common/frame_messages.h"
19 #include "content/common/view_messages.h"
20 #include "content/public/browser/notification_details.h"
21 #include "content/public/browser/notification_service.h"
22 #include "content/public/browser/notification_source.h"
23 #include "content/public/browser/notification_types.h"
24 #include "content/public/browser/render_process_host.h"
25 #include "content/public/browser/render_widget_host_iterator.h"
26 #include "content/public/browser/web_contents_delegate.h"
27 #include "content/public/browser/web_contents_observer.h"
28 #include "content/public/browser/web_ui_controller.h"
29 #include "content/public/common/bindings_policy.h"
30 #include "content/public/common/content_switches.h"
31 #include "content/public/common/javascript_message_type.h"
32 #include "content/public/common/url_constants.h"
33 #include "content/public/common/url_utils.h"
34 #include "content/public/test/mock_render_process_host.h"
35 #include "content/public/test/test_notification_tracker.h"
36 #include "content/test/test_content_browser_client.h"
37 #include "content/test/test_content_client.h"
38 #include "content/test/test_render_frame_host.h"
39 #include "content/test/test_render_view_host.h"
40 #include "content/test/test_web_contents.h"
41 #include "net/base/load_flags.h"
42 #include "testing/gtest/include/gtest/gtest.h"
43 #include "ui/base/page_transition_types.h"
48 class RenderFrameHostManagerTestWebUIControllerFactory
49 : public WebUIControllerFactory
{
51 RenderFrameHostManagerTestWebUIControllerFactory()
52 : should_create_webui_(false) {
54 ~RenderFrameHostManagerTestWebUIControllerFactory() override
{}
56 void set_should_create_webui(bool should_create_webui
) {
57 should_create_webui_
= should_create_webui
;
60 // WebUIFactory implementation.
61 WebUIController
* CreateWebUIControllerForURL(WebUI
* web_ui
,
62 const GURL
& url
) const override
{
63 if (!(should_create_webui_
&& HasWebUIScheme(url
)))
65 return new WebUIController(web_ui
);
68 WebUI::TypeID
GetWebUIType(BrowserContext
* browser_context
,
69 const GURL
& url
) const override
{
70 return WebUI::kNoWebUI
;
73 bool UseWebUIForURL(BrowserContext
* browser_context
,
74 const GURL
& url
) const override
{
75 return HasWebUIScheme(url
);
78 bool UseWebUIBindingsForURL(BrowserContext
* browser_context
,
79 const GURL
& url
) const override
{
80 return HasWebUIScheme(url
);
84 bool should_create_webui_
;
86 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostManagerTestWebUIControllerFactory
);
89 class BeforeUnloadFiredWebContentsDelegate
: public WebContentsDelegate
{
91 BeforeUnloadFiredWebContentsDelegate() {}
92 ~BeforeUnloadFiredWebContentsDelegate() override
{}
94 void BeforeUnloadFired(WebContents
* web_contents
,
96 bool* proceed_to_fire_unload
) override
{
97 *proceed_to_fire_unload
= proceed
;
101 DISALLOW_COPY_AND_ASSIGN(BeforeUnloadFiredWebContentsDelegate
);
104 class CloseWebContentsDelegate
: public WebContentsDelegate
{
106 CloseWebContentsDelegate() : close_called_(false) {}
107 ~CloseWebContentsDelegate() override
{}
109 void CloseContents(WebContents
* web_contents
) override
{
110 close_called_
= true;
113 bool is_closed() { return close_called_
; }
116 DISALLOW_COPY_AND_ASSIGN(CloseWebContentsDelegate
);
121 // This observer keeps track of the last deleted RenderViewHost to avoid
122 // accessing it and causing use-after-free condition.
123 class RenderViewHostDeletedObserver
: public WebContentsObserver
{
125 RenderViewHostDeletedObserver(RenderViewHost
* rvh
)
126 : WebContentsObserver(WebContents::FromRenderViewHost(rvh
)),
127 process_id_(rvh
->GetProcess()->GetID()),
128 routing_id_(rvh
->GetRoutingID()),
132 void RenderViewDeleted(RenderViewHost
* render_view_host
) override
{
133 if (render_view_host
->GetProcess()->GetID() == process_id_
&&
134 render_view_host
->GetRoutingID() == routing_id_
) {
148 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDeletedObserver
);
151 // This observer keeps track of the last created RenderFrameHost to allow tests
152 // to ensure that no RenderFrameHost objects are created when not expected.
153 class RenderFrameHostCreatedObserver
: public WebContentsObserver
{
155 RenderFrameHostCreatedObserver(WebContents
* web_contents
)
156 : WebContentsObserver(web_contents
),
160 void RenderFrameCreated(RenderFrameHost
* render_frame_host
) override
{
171 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostCreatedObserver
);
174 // This observer keeps track of the last deleted RenderFrameHost to avoid
175 // accessing it and causing use-after-free condition.
176 class RenderFrameHostDeletedObserver
: public WebContentsObserver
{
178 RenderFrameHostDeletedObserver(RenderFrameHost
* rfh
)
179 : WebContentsObserver(WebContents::FromRenderFrameHost(rfh
)),
180 process_id_(rfh
->GetProcess()->GetID()),
181 routing_id_(rfh
->GetRoutingID()),
185 void RenderFrameDeleted(RenderFrameHost
* render_frame_host
) override
{
186 if (render_frame_host
->GetProcess()->GetID() == process_id_
&&
187 render_frame_host
->GetRoutingID() == routing_id_
) {
201 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostDeletedObserver
);
204 // This observer is used to check whether IPC messages are being filtered for
205 // swapped out RenderFrameHost objects. It observes the plugin crash and favicon
206 // update events, which the FilterMessagesWhileSwappedOut test simulates being
207 // sent. The test is successful if the event is not observed.
208 // See http://crbug.com/351815
209 class PluginFaviconMessageObserver
: public WebContentsObserver
{
211 PluginFaviconMessageObserver(WebContents
* web_contents
)
212 : WebContentsObserver(web_contents
),
213 plugin_crashed_(false),
214 favicon_received_(false) { }
216 void PluginCrashed(const base::FilePath
& plugin_path
,
217 base::ProcessId plugin_pid
) override
{
218 plugin_crashed_
= true;
221 void DidUpdateFaviconURL(const std::vector
<FaviconURL
>& candidates
) override
{
222 favicon_received_
= true;
225 bool plugin_crashed() {
226 return plugin_crashed_
;
229 bool favicon_received() {
230 return favicon_received_
;
234 bool plugin_crashed_
;
235 bool favicon_received_
;
237 DISALLOW_COPY_AND_ASSIGN(PluginFaviconMessageObserver
);
240 // Ensures that RenderFrameDeleted and RenderFrameCreated are called in a
241 // consistent manner.
242 class FrameLifetimeConsistencyChecker
: public WebContentsObserver
{
244 explicit FrameLifetimeConsistencyChecker(WebContentsImpl
* web_contents
)
245 : WebContentsObserver(web_contents
) {
246 RenderViewCreated(web_contents
->GetRenderViewHost());
247 RenderFrameCreated(web_contents
->GetMainFrame());
250 void RenderFrameCreated(RenderFrameHost
* render_frame_host
) override
{
251 std::pair
<int, int> routing_pair
=
252 std::make_pair(render_frame_host
->GetProcess()->GetID(),
253 render_frame_host
->GetRoutingID());
254 bool was_live_already
= !live_routes_
.insert(routing_pair
).second
;
255 bool was_used_before
= deleted_routes_
.count(routing_pair
) != 0;
257 if (was_live_already
) {
258 FAIL() << "RenderFrameCreated called more than once for routing pair: "
259 << Format(render_frame_host
);
260 } else if (was_used_before
) {
261 FAIL() << "RenderFrameCreated called for routing pair "
262 << Format(render_frame_host
) << " that was previously deleted.";
266 void RenderFrameDeleted(RenderFrameHost
* render_frame_host
) override
{
267 std::pair
<int, int> routing_pair
=
268 std::make_pair(render_frame_host
->GetProcess()->GetID(),
269 render_frame_host
->GetRoutingID());
270 bool was_live
= live_routes_
.erase(routing_pair
);
271 bool was_dead_already
= !deleted_routes_
.insert(routing_pair
).second
;
273 if (was_dead_already
) {
274 FAIL() << "RenderFrameDeleted called more than once for routing pair "
275 << Format(render_frame_host
);
276 } else if (!was_live
) {
277 FAIL() << "RenderFrameDeleted called for routing pair "
278 << Format(render_frame_host
)
279 << " for which RenderFrameCreated was never called";
284 std::string
Format(RenderFrameHost
* render_frame_host
) {
285 return base::StringPrintf(
287 render_frame_host
->GetProcess()->GetID(),
288 render_frame_host
->GetRoutingID(),
289 render_frame_host
->GetSiteInstance()->GetSiteURL().spec().c_str());
291 std::set
<std::pair
<int, int> > live_routes_
;
292 std::set
<std::pair
<int, int> > deleted_routes_
;
297 class RenderFrameHostManagerTest
298 : public RenderViewHostImplTestHarness
{
300 virtual void SetUp() override
{
301 RenderViewHostImplTestHarness::SetUp();
302 WebUIControllerFactory::RegisterFactory(&factory_
);
303 lifetime_checker_
.reset(new FrameLifetimeConsistencyChecker(contents()));
306 virtual void TearDown() override
{
307 lifetime_checker_
.reset();
308 RenderViewHostImplTestHarness::TearDown();
309 WebUIControllerFactory::UnregisterFactoryForTesting(&factory_
);
312 void set_should_create_webui(bool should_create_webui
) {
313 factory_
.set_should_create_webui(should_create_webui
);
316 void NavigateActiveAndCommit(const GURL
& url
) {
317 // Note: we navigate the active RenderFrameHost because previous navigations
318 // won't have committed yet, so NavigateAndCommit does the wrong thing
320 controller().LoadURL(
321 url
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
322 TestRenderFrameHost
* old_rfh
= contents()->GetMainFrame();
323 TestRenderFrameHost
* active_rfh
= contents()->GetPendingMainFrame()
324 ? contents()->GetPendingMainFrame()
327 // Simulate the BeforeUnload_ACK that is received from the current renderer
328 // for a cross-site navigation.
329 if (old_rfh
!= active_rfh
) {
330 old_rfh
->SendBeforeUnloadACK(true);
331 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, old_rfh
->rfh_state());
334 // Commit the navigation with a new page ID.
335 int32 max_page_id
= contents()->GetMaxPageIDForSiteInstance(
336 active_rfh
->GetSiteInstance());
338 // Use an observer to avoid accessing a deleted renderer later on when the
339 // state is being checked.
340 RenderFrameHostDeletedObserver
rfh_observer(old_rfh
);
341 RenderViewHostDeletedObserver
rvh_observer(old_rfh
->GetRenderViewHost());
342 active_rfh
->SendNavigate(max_page_id
+ 1, url
);
344 // Make sure that we start to run the unload handler at the time of commit.
345 bool expecting_rfh_shutdown
= false;
346 if (old_rfh
!= active_rfh
&& !rfh_observer
.deleted()) {
347 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
,
348 old_rfh
->rfh_state());
349 if (!old_rfh
->GetSiteInstance()->active_frame_count()) {
350 expecting_rfh_shutdown
= true;
352 old_rfh
->frame_tree_node()->render_manager()->IsPendingDeletion(
357 // Simulate the swap out ACK coming from the pending renderer. This should
358 // either shut down the old RFH or leave it in a swapped out state.
359 if (old_rfh
!= active_rfh
) {
360 old_rfh
->OnSwappedOut();
361 if (expecting_rfh_shutdown
) {
362 EXPECT_TRUE(rfh_observer
.deleted());
363 EXPECT_TRUE(rvh_observer
.deleted());
365 EXPECT_EQ(RenderFrameHostImpl::STATE_SWAPPED_OUT
,
366 old_rfh
->rfh_state());
369 EXPECT_EQ(active_rfh
, contents()->GetMainFrame());
370 EXPECT_EQ(NULL
, contents()->GetPendingMainFrame());
373 bool ShouldSwapProcesses(RenderFrameHostManager
* manager
,
374 const NavigationEntryImpl
* current_entry
,
375 const NavigationEntryImpl
* new_entry
) const {
377 BrowserContext
* browser_context
=
378 manager
->delegate_
->GetControllerForRenderManager().GetBrowserContext();
379 const GURL
& current_effective_url
= current_entry
?
380 SiteInstanceImpl::GetEffectiveURL(browser_context
,
381 current_entry
->GetURL()) :
382 manager
->render_frame_host_
->GetSiteInstance()->GetSiteURL();
383 bool current_is_view_source_mode
= current_entry
?
384 current_entry
->IsViewSourceMode() : new_entry
->IsViewSourceMode();
385 return manager
->ShouldSwapBrowsingInstancesForNavigation(
386 current_effective_url
,
387 current_is_view_source_mode
,
388 new_entry
->site_instance(),
389 SiteInstanceImpl::GetEffectiveURL(browser_context
, new_entry
->GetURL()),
390 new_entry
->IsViewSourceMode());
393 // Creates a test RenderFrameHost that's swapped out.
394 TestRenderFrameHost
* CreateSwappedOutRenderFrameHost() {
395 const GURL
kChromeURL("chrome://foo");
396 const GURL
kDestUrl("http://www.google.com/");
398 // Navigate our first tab to a chrome url and then to the destination.
399 NavigateActiveAndCommit(kChromeURL
);
400 TestRenderFrameHost
* ntp_rfh
= contents()->GetMainFrame();
402 // Navigate to a cross-site URL.
403 contents()->GetController().LoadURL(
404 kDestUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
405 EXPECT_TRUE(contents()->cross_navigation_pending());
407 // Manually increase the number of active frames in the
408 // SiteInstance that ntp_rfh belongs to, to prevent it from being
409 // destroyed when it gets swapped out.
410 ntp_rfh
->GetSiteInstance()->increment_active_frame_count();
412 TestRenderFrameHost
* dest_rfh
= contents()->GetPendingMainFrame();
414 EXPECT_NE(ntp_rfh
, dest_rfh
);
416 // BeforeUnload finishes.
417 ntp_rfh
->SendBeforeUnloadACK(true);
419 dest_rfh
->SendNavigate(101, kDestUrl
);
420 ntp_rfh
->OnSwappedOut();
422 EXPECT_TRUE(ntp_rfh
->is_swapped_out());
427 RenderFrameHostManagerTestWebUIControllerFactory factory_
;
428 scoped_ptr
<FrameLifetimeConsistencyChecker
> lifetime_checker_
;
431 // Tests that when you navigate from a chrome:// url to another page, and
432 // then do that same thing in another tab, that the two resulting pages have
433 // different SiteInstances, BrowsingInstances, and RenderProcessHosts. This is
434 // a regression test for bug 9364.
435 TEST_F(RenderFrameHostManagerTest
, NewTabPageProcesses
) {
436 set_should_create_webui(true);
437 const GURL
kChromeUrl("chrome://foo");
438 const GURL
kDestUrl("http://www.google.com/");
440 // Navigate our first tab to the chrome url and then to the destination,
441 // ensuring we grant bindings to the chrome URL.
442 NavigateActiveAndCommit(kChromeUrl
);
443 EXPECT_TRUE(active_rvh()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
444 NavigateActiveAndCommit(kDestUrl
);
446 EXPECT_FALSE(contents()->GetPendingMainFrame());
448 // Make a second tab.
449 scoped_ptr
<TestWebContents
> contents2(
450 TestWebContents::Create(browser_context(), NULL
));
452 // Load the two URLs in the second tab. Note that the first navigation creates
453 // a RFH that's not pending (since there is no cross-site transition), so
454 // we use the committed one.
455 contents2
->GetController().LoadURL(
456 kChromeUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
457 TestRenderFrameHost
* ntp_rfh2
= contents2
->GetMainFrame();
458 EXPECT_FALSE(contents2
->cross_navigation_pending());
459 ntp_rfh2
->SendNavigate(100, kChromeUrl
);
461 // The second one is the opposite, creating a cross-site transition and
462 // requiring a beforeunload ack.
463 contents2
->GetController().LoadURL(
464 kDestUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
465 EXPECT_TRUE(contents2
->cross_navigation_pending());
466 TestRenderFrameHost
* dest_rfh2
= contents2
->GetPendingMainFrame();
467 ASSERT_TRUE(dest_rfh2
);
469 ntp_rfh2
->SendBeforeUnloadACK(true);
470 dest_rfh2
->SendNavigate(101, kDestUrl
);
472 // The two RFH's should be different in every way.
473 EXPECT_NE(contents()->GetMainFrame()->GetProcess(), dest_rfh2
->GetProcess());
474 EXPECT_NE(contents()->GetMainFrame()->GetSiteInstance(),
475 dest_rfh2
->GetSiteInstance());
476 EXPECT_FALSE(dest_rfh2
->GetSiteInstance()->IsRelatedSiteInstance(
477 contents()->GetMainFrame()->GetSiteInstance()));
479 // Navigate both to the new tab page, and verify that they share a
480 // RenderProcessHost (not a SiteInstance).
481 NavigateActiveAndCommit(kChromeUrl
);
482 EXPECT_FALSE(contents()->GetPendingMainFrame());
484 contents2
->GetController().LoadURL(
485 kChromeUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
486 dest_rfh2
->SendBeforeUnloadACK(true);
487 contents2
->GetPendingMainFrame()->SendNavigate(102, kChromeUrl
);
489 EXPECT_NE(contents()->GetMainFrame()->GetSiteInstance(),
490 contents2
->GetMainFrame()->GetSiteInstance());
491 EXPECT_EQ(contents()->GetMainFrame()->GetSiteInstance()->GetProcess(),
492 contents2
->GetMainFrame()->GetSiteInstance()->GetProcess());
495 // Ensure that the browser ignores most IPC messages that arrive from a
496 // RenderViewHost that has been swapped out. We do not want to take
497 // action on requests from a non-active renderer. The main exception is
498 // for synchronous messages, which cannot be ignored without leaving the
499 // renderer in a stuck state. See http://crbug.com/93427.
500 TEST_F(RenderFrameHostManagerTest
, FilterMessagesWhileSwappedOut
) {
501 const GURL
kChromeURL("chrome://foo");
502 const GURL
kDestUrl("http://www.google.com/");
503 std::vector
<FaviconURL
> icons
;
505 // Navigate our first tab to a chrome url and then to the destination.
506 NavigateActiveAndCommit(kChromeURL
);
507 TestRenderFrameHost
* ntp_rfh
= contents()->GetMainFrame();
509 // Send an update favicon message and make sure it works.
510 const base::string16 ntp_title
= base::ASCIIToUTF16("NTP Title");
512 PluginFaviconMessageObserver
observer(contents());
513 EXPECT_TRUE(ntp_rfh
->GetRenderViewHost()->OnMessageReceived(
514 ViewHostMsg_UpdateFaviconURL(
515 ntp_rfh
->GetRenderViewHost()->GetRoutingID(), icons
)));
516 EXPECT_TRUE(observer
.favicon_received());
518 // Create one more frame in the same SiteInstance where ntp_rfh
519 // exists so that it doesn't get deleted on navigation to another
521 ntp_rfh
->GetSiteInstance()->increment_active_frame_count();
524 // Navigate to a cross-site URL.
525 NavigateActiveAndCommit(kDestUrl
);
526 TestRenderFrameHost
* dest_rfh
= contents()->GetMainFrame();
527 ASSERT_TRUE(dest_rfh
);
528 EXPECT_NE(ntp_rfh
, dest_rfh
);
530 // The new RVH should be able to update its favicon.
531 const base::string16 dest_title
= base::ASCIIToUTF16("Google");
533 PluginFaviconMessageObserver
observer(contents());
535 dest_rfh
->GetRenderViewHost()->OnMessageReceived(
536 ViewHostMsg_UpdateFaviconURL(
537 dest_rfh
->GetRenderViewHost()->GetRoutingID(), icons
)));
538 EXPECT_TRUE(observer
.favicon_received());
541 // The old renderer, being slow, now updates the favicon. It should be
542 // filtered out and not take effect.
543 EXPECT_TRUE(ntp_rfh
->is_swapped_out());
545 PluginFaviconMessageObserver
observer(contents());
547 ntp_rfh
->GetRenderViewHost()->OnMessageReceived(
548 ViewHostMsg_UpdateFaviconURL(
549 dest_rfh
->GetRenderViewHost()->GetRoutingID(), icons
)));
550 EXPECT_FALSE(observer
.favicon_received());
553 // The same logic should apply to RenderFrameHosts as well and routing through
554 // swapped out RFH shouldn't be allowed. Use a PluginCrashObserver to check
555 // if the IPC message is allowed through or not.
557 PluginFaviconMessageObserver
observer(contents());
558 EXPECT_TRUE(ntp_rfh
->OnMessageReceived(
559 FrameHostMsg_PluginCrashed(
560 ntp_rfh
->GetRoutingID(), base::FilePath(), 0)));
561 EXPECT_FALSE(observer
.plugin_crashed());
564 // We cannot filter out synchronous IPC messages, because the renderer would
565 // be left waiting for a reply. We pick RunBeforeUnloadConfirm as an example
566 // that can run easily within a unit test, and that needs to receive a reply
567 // without showing an actual dialog.
568 MockRenderProcessHost
* ntp_process_host
=
569 static_cast<MockRenderProcessHost
*>(ntp_rfh
->GetProcess());
570 ntp_process_host
->sink().ClearMessages();
571 const base::string16 msg
= base::ASCIIToUTF16("Message");
573 base::string16 unused
;
574 FrameHostMsg_RunBeforeUnloadConfirm
before_unload_msg(
575 ntp_rfh
->GetRoutingID(), kChromeURL
, msg
, false, &result
, &unused
);
576 // Enable pumping for check in BrowserMessageFilter::CheckCanDispatchOnUI.
577 before_unload_msg
.EnableMessagePumping();
578 EXPECT_TRUE(ntp_rfh
->OnMessageReceived(before_unload_msg
));
579 EXPECT_TRUE(ntp_process_host
->sink().GetUniqueMessageMatching(IPC_REPLY_ID
));
581 // Also test RunJavaScriptMessage.
582 ntp_process_host
->sink().ClearMessages();
583 FrameHostMsg_RunJavaScriptMessage
js_msg(
584 ntp_rfh
->GetRoutingID(), msg
, msg
, kChromeURL
,
585 JAVASCRIPT_MESSAGE_TYPE_CONFIRM
, &result
, &unused
);
586 js_msg
.EnableMessagePumping();
587 EXPECT_TRUE(ntp_rfh
->OnMessageReceived(js_msg
));
588 EXPECT_TRUE(ntp_process_host
->sink().GetUniqueMessageMatching(IPC_REPLY_ID
));
591 // Ensure that frames aren't added to the frame tree, if the message is coming
592 // from a process different than the parent frame's current RenderFrameHost
593 // process. Otherwise it is possible to have collisions of routing ids, as they
594 // are scoped per process. See https://crbug.com/415059.
595 TEST_F(RenderFrameHostManagerTest
, DropCreateChildFrameWhileSwappedOut
) {
596 const GURL
kUrl1("http://foo.com");
597 const GURL
kUrl2("http://www.google.com/");
599 // Navigate to the first site.
600 NavigateActiveAndCommit(kUrl1
);
601 TestRenderFrameHost
* initial_rfh
= contents()->GetMainFrame();
603 RenderFrameHostCreatedObserver
observer(contents());
604 initial_rfh
->OnCreateChildFrame(
605 initial_rfh
->GetProcess()->GetNextRoutingID(), std::string());
606 EXPECT_TRUE(observer
.created());
609 // Create one more frame in the same SiteInstance where initial_rfh
610 // exists so that initial_rfh doesn't get deleted on navigation to another
612 initial_rfh
->GetSiteInstance()->increment_active_frame_count();
614 // Navigate to a cross-site URL.
615 NavigateActiveAndCommit(kUrl2
);
616 EXPECT_TRUE(initial_rfh
->is_swapped_out());
618 TestRenderFrameHost
* dest_rfh
= contents()->GetMainFrame();
619 ASSERT_TRUE(dest_rfh
);
620 EXPECT_NE(initial_rfh
, dest_rfh
);
623 // Since the old RFH is now swapped out, it shouldn't process any messages
624 // to create child frames.
625 RenderFrameHostCreatedObserver
observer(contents());
626 initial_rfh
->OnCreateChildFrame(
627 initial_rfh
->GetProcess()->GetNextRoutingID(), std::string());
628 EXPECT_FALSE(observer
.created());
632 TEST_F(RenderFrameHostManagerTest
, WhiteListSwapCompositorFrame
) {
633 TestRenderFrameHost
* swapped_out_rfh
= CreateSwappedOutRenderFrameHost();
634 TestRenderWidgetHostView
* swapped_out_rwhv
=
635 static_cast<TestRenderWidgetHostView
*>(
636 swapped_out_rfh
->GetRenderViewHost()->GetView());
637 EXPECT_FALSE(swapped_out_rwhv
->did_swap_compositor_frame());
639 MockRenderProcessHost
* process_host
=
640 static_cast<MockRenderProcessHost
*>(swapped_out_rfh
->GetProcess());
641 process_host
->sink().ClearMessages();
643 cc::CompositorFrame frame
;
644 ViewHostMsg_SwapCompositorFrame
msg(
645 rvh()->GetRoutingID(), 0, frame
, std::vector
<IPC::Message
>());
647 EXPECT_TRUE(swapped_out_rfh
->render_view_host()->OnMessageReceived(msg
));
648 EXPECT_TRUE(swapped_out_rwhv
->did_swap_compositor_frame());
651 // Test if RenderViewHost::GetRenderWidgetHosts() only returns active
653 TEST_F(RenderFrameHostManagerTest
, GetRenderWidgetHostsReturnsActiveViews
) {
654 TestRenderFrameHost
* swapped_out_rfh
= CreateSwappedOutRenderFrameHost();
655 EXPECT_TRUE(swapped_out_rfh
->is_swapped_out());
657 scoped_ptr
<RenderWidgetHostIterator
> widgets(
658 RenderWidgetHost::GetRenderWidgetHosts());
659 // We know that there is the only one active widget. Another view is
660 // now swapped out, so the swapped out view is not included in the
662 RenderWidgetHost
* widget
= widgets
->GetNextHost();
663 EXPECT_FALSE(widgets
->GetNextHost());
664 RenderViewHost
* rvh
= RenderViewHost::From(widget
);
665 EXPECT_TRUE(static_cast<RenderViewHostImpl
*>(rvh
)->is_active());
668 // Test if RenderViewHost::GetRenderWidgetHosts() returns a subset of
669 // RenderViewHostImpl::GetAllRenderWidgetHosts().
670 // RenderViewHost::GetRenderWidgetHosts() returns only active widgets, but
671 // RenderViewHostImpl::GetAllRenderWidgetHosts() returns everything
672 // including swapped out ones.
673 TEST_F(RenderFrameHostManagerTest
,
674 GetRenderWidgetHostsWithinGetAllRenderWidgetHosts
) {
675 TestRenderFrameHost
* swapped_out_rfh
= CreateSwappedOutRenderFrameHost();
676 EXPECT_TRUE(swapped_out_rfh
->is_swapped_out());
678 scoped_ptr
<RenderWidgetHostIterator
> widgets(
679 RenderWidgetHost::GetRenderWidgetHosts());
681 while (RenderWidgetHost
* w
= widgets
->GetNextHost()) {
683 scoped_ptr
<RenderWidgetHostIterator
> all_widgets(
684 RenderWidgetHostImpl::GetAllRenderWidgetHosts());
685 while (RenderWidgetHost
* widget
= all_widgets
->GetNextHost()) {
695 // Test if SiteInstanceImpl::active_frame_count() is correctly updated
696 // as frames in a SiteInstance get swapped out and in.
697 TEST_F(RenderFrameHostManagerTest
, ActiveFrameCountWhileSwappingInAndOut
) {
698 const GURL
kUrl1("http://www.google.com/");
699 const GURL
kUrl2("http://www.chromium.org/");
701 // Navigate to an initial URL.
702 contents()->NavigateAndCommit(kUrl1
);
703 TestRenderFrameHost
* rfh1
= main_test_rfh();
705 SiteInstanceImpl
* instance1
= rfh1
->GetSiteInstance();
706 EXPECT_EQ(instance1
->active_frame_count(), 1U);
708 // Create 2 new tabs and simulate them being the opener chain for the main
709 // tab. They should be in the same SiteInstance.
710 scoped_ptr
<TestWebContents
> opener1(
711 TestWebContents::Create(browser_context(), instance1
));
712 contents()->SetOpener(opener1
.get());
714 scoped_ptr
<TestWebContents
> opener2(
715 TestWebContents::Create(browser_context(), instance1
));
716 opener1
->SetOpener(opener2
.get());
718 EXPECT_EQ(instance1
->active_frame_count(), 3U);
720 // Navigate to a cross-site URL (different SiteInstance but same
721 // BrowsingInstance).
722 contents()->NavigateAndCommit(kUrl2
);
723 TestRenderFrameHost
* rfh2
= main_test_rfh();
724 SiteInstanceImpl
* instance2
= rfh2
->GetSiteInstance();
726 // rvh2 is on chromium.org which is different from google.com on
727 // which other tabs are.
728 EXPECT_EQ(instance2
->active_frame_count(), 1U);
730 // There are two active views on google.com now.
731 EXPECT_EQ(instance1
->active_frame_count(), 2U);
733 // Navigate to the original origin (google.com).
734 contents()->NavigateAndCommit(kUrl1
);
736 EXPECT_EQ(instance1
->active_frame_count(), 3U);
739 // This deletes a WebContents when the given RVH is deleted. This is
740 // only for testing whether deleting an RVH does not cause any UaF in
741 // other parts of the system. For now, this class is only used for the
742 // next test cases to detect the bug mentioned at
743 // http://crbug.com/259859.
744 class RenderViewHostDestroyer
: public WebContentsObserver
{
746 RenderViewHostDestroyer(RenderViewHost
* render_view_host
,
747 WebContents
* web_contents
)
748 : WebContentsObserver(WebContents::FromRenderViewHost(render_view_host
)),
749 render_view_host_(render_view_host
),
750 web_contents_(web_contents
) {}
752 void RenderViewDeleted(RenderViewHost
* render_view_host
) override
{
753 if (render_view_host
== render_view_host_
)
754 delete web_contents_
;
758 RenderViewHost
* render_view_host_
;
759 WebContents
* web_contents_
;
761 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestroyer
);
764 // Test if ShutdownRenderViewHostsInSiteInstance() does not touch any
765 // RenderWidget that has been freed while deleting a RenderViewHost in
766 // a previous iteration. This is a regression test for
767 // http://crbug.com/259859.
768 TEST_F(RenderFrameHostManagerTest
,
769 DetectUseAfterFreeInShutdownRenderViewHostsInSiteInstance
) {
770 const GURL
kChromeURL("chrome://newtab");
771 const GURL
kUrl1("http://www.google.com");
772 const GURL
kUrl2("http://www.chromium.org");
774 // Navigate our first tab to a chrome url and then to the destination.
775 NavigateActiveAndCommit(kChromeURL
);
776 TestRenderFrameHost
* ntp_rfh
= contents()->GetMainFrame();
778 // Create one more tab and navigate to kUrl1. web_contents is not
779 // wrapped as scoped_ptr since it intentionally deleted by destroyer
780 // below as part of this test.
781 TestWebContents
* web_contents
=
782 TestWebContents::Create(browser_context(), ntp_rfh
->GetSiteInstance());
783 web_contents
->NavigateAndCommit(kUrl1
);
784 RenderViewHostDestroyer
destroyer(ntp_rfh
->GetRenderViewHost(),
787 // This causes the first tab to navigate to kUrl2, which destroys
788 // the ntp_rfh in ShutdownRenderViewHostsInSiteInstance(). When
789 // ntp_rfh is destroyed, it also destroys the RVHs in web_contents
790 // too. This can test whether
791 // SiteInstanceImpl::ShutdownRenderViewHostsInSiteInstance() can
792 // touch any object freed in this way or not while iterating through
794 contents()->NavigateAndCommit(kUrl2
);
797 // When there is an error with the specified page, renderer exits view-source
798 // mode. See WebFrameImpl::DidFail(). We check by this test that
799 // EnableViewSourceMode message is sent on every navigation regardless
800 // RenderView is being newly created or reused.
801 TEST_F(RenderFrameHostManagerTest
, AlwaysSendEnableViewSourceMode
) {
802 const GURL
kChromeUrl("chrome://foo");
803 const GURL
kUrl("view-source:http://foo");
805 // We have to navigate to some page at first since without this, the first
806 // navigation will reuse the SiteInstance created by Init(), and the second
807 // one will create a new SiteInstance. Because current_instance and
808 // new_instance will be different, a new RenderViewHost will be created for
809 // the second navigation. We have to avoid this in order to exercise the
810 // target code patch.
811 NavigateActiveAndCommit(kChromeUrl
);
814 controller().LoadURL(
815 kUrl
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
816 // Simulate response from RenderFrame for DispatchBeforeUnload.
817 base::TimeTicks now
= base::TimeTicks::Now();
818 contents()->GetMainFrame()->OnMessageReceived(FrameHostMsg_BeforeUnload_ACK(
819 contents()->GetMainFrame()->GetRoutingID(), true, now
, now
));
820 ASSERT_TRUE(contents()->GetPendingMainFrame())
821 << "Expected new pending RenderFrameHost to be created.";
822 RenderFrameHost
* last_rfh
= contents()->GetPendingMainFrame();
824 contents()->GetMaxPageIDForSiteInstance(last_rfh
->GetSiteInstance()) + 1;
825 contents()->GetPendingMainFrame()->SendNavigate(new_id
, kUrl
);
826 EXPECT_EQ(controller().GetLastCommittedEntryIndex(), 1);
827 ASSERT_TRUE(controller().GetLastCommittedEntry());
828 EXPECT_TRUE(kUrl
== controller().GetLastCommittedEntry()->GetURL());
829 EXPECT_FALSE(controller().GetPendingEntry());
830 // Because we're using TestWebContents and TestRenderViewHost in this
831 // unittest, no one calls WebContentsImpl::RenderViewCreated(). So, we see no
832 // EnableViewSourceMode message, here.
834 // Clear queued messages before load.
835 process()->sink().ClearMessages();
837 controller().LoadURL(
838 kUrl
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
839 // The same RenderViewHost should be reused.
840 EXPECT_FALSE(contents()->GetPendingMainFrame());
841 EXPECT_TRUE(last_rfh
== contents()->GetMainFrame());
842 // Navigate using the returned page_id.
843 contents()->GetMainFrame()->SendNavigate(new_id
, kUrl
);
844 EXPECT_EQ(controller().GetLastCommittedEntryIndex(), 1);
845 EXPECT_FALSE(controller().GetPendingEntry());
846 // New message should be sent out to make sure to enter view-source mode.
847 EXPECT_TRUE(process()->sink().GetUniqueMessageMatching(
848 ViewMsg_EnableViewSourceMode::ID
));
851 // Tests the Init function by checking the initial RenderViewHost.
852 TEST_F(RenderFrameHostManagerTest
, Init
) {
853 // Using TestBrowserContext.
854 SiteInstanceImpl
* instance
=
855 static_cast<SiteInstanceImpl
*>(SiteInstance::Create(browser_context()));
856 EXPECT_FALSE(instance
->HasSite());
858 scoped_ptr
<TestWebContents
> web_contents(
859 TestWebContents::Create(browser_context(), instance
));
861 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
862 RenderViewHostImpl
* rvh
= manager
->current_host();
863 RenderFrameHostImpl
* rfh
= manager
->current_frame_host();
866 EXPECT_EQ(rvh
, rfh
->render_view_host());
867 EXPECT_EQ(instance
, rvh
->GetSiteInstance());
868 EXPECT_EQ(web_contents
.get(), rvh
->GetDelegate());
869 EXPECT_EQ(web_contents
.get(), rfh
->delegate());
870 EXPECT_TRUE(manager
->GetRenderWidgetHostView());
871 EXPECT_FALSE(manager
->pending_render_view_host());
874 // Tests the Navigate function. We navigate three sites consecutively and check
875 // how the pending/committed RenderViewHost are modified.
876 TEST_F(RenderFrameHostManagerTest
, Navigate
) {
877 TestNotificationTracker notifications
;
879 SiteInstance
* instance
= SiteInstance::Create(browser_context());
881 scoped_ptr
<TestWebContents
> web_contents(
882 TestWebContents::Create(browser_context(), instance
));
883 notifications
.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED
,
884 Source
<WebContents
>(web_contents
.get()));
886 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
887 RenderFrameHostImpl
* host
= NULL
;
889 // 1) The first navigation. --------------------------
890 const GURL
kUrl1("http://www.google.com/");
891 NavigationEntryImpl
entry1(
892 NULL
/* instance */, -1 /* page_id */, kUrl1
, Referrer(),
893 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED
,
894 false /* is_renderer_init */);
895 host
= manager
->Navigate(entry1
);
897 // The RenderFrameHost created in Init will be reused.
898 EXPECT_TRUE(host
== manager
->current_frame_host());
899 EXPECT_FALSE(manager
->pending_frame_host());
902 manager
->DidNavigateFrame(host
);
903 // Commit to SiteInstance should be delayed until RenderView commit.
904 EXPECT_TRUE(host
== manager
->current_frame_host());
906 EXPECT_FALSE(host
->GetSiteInstance()->HasSite());
907 host
->GetSiteInstance()->SetSite(kUrl1
);
909 // 2) Navigate to next site. -------------------------
910 const GURL
kUrl2("http://www.google.com/foo");
911 NavigationEntryImpl
entry2(
912 NULL
/* instance */, -1 /* page_id */, kUrl2
,
913 Referrer(kUrl1
, blink::WebReferrerPolicyDefault
),
914 base::string16() /* title */, ui::PAGE_TRANSITION_LINK
,
915 true /* is_renderer_init */);
916 host
= manager
->Navigate(entry2
);
918 // The RenderFrameHost created in Init will be reused.
919 EXPECT_TRUE(host
== manager
->current_frame_host());
920 EXPECT_FALSE(manager
->pending_frame_host());
923 manager
->DidNavigateFrame(host
);
924 EXPECT_TRUE(host
== manager
->current_frame_host());
926 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
928 // 3) Cross-site navigate to next site. --------------
929 const GURL
kUrl3("http://webkit.org/");
930 NavigationEntryImpl
entry3(
931 NULL
/* instance */, -1 /* page_id */, kUrl3
,
932 Referrer(kUrl2
, blink::WebReferrerPolicyDefault
),
933 base::string16() /* title */, ui::PAGE_TRANSITION_LINK
,
934 false /* is_renderer_init */);
935 host
= manager
->Navigate(entry3
);
937 // A new RenderFrameHost should be created.
938 EXPECT_TRUE(manager
->pending_frame_host());
939 ASSERT_EQ(host
, manager
->pending_frame_host());
941 notifications
.Reset();
944 manager
->DidNavigateFrame(manager
->pending_frame_host());
945 EXPECT_TRUE(host
== manager
->current_frame_host());
947 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
948 // Check the pending RenderFrameHost has been committed.
949 EXPECT_FALSE(manager
->pending_frame_host());
951 // We should observe a notification.
953 notifications
.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED
));
956 // Tests WebUI creation.
957 TEST_F(RenderFrameHostManagerTest
, WebUI
) {
958 set_should_create_webui(true);
959 SiteInstance
* instance
= SiteInstance::Create(browser_context());
961 scoped_ptr
<TestWebContents
> web_contents(
962 TestWebContents::Create(browser_context(), instance
));
963 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
965 EXPECT_FALSE(manager
->current_host()->IsRenderViewLive());
967 const GURL
kUrl("chrome://foo");
968 NavigationEntryImpl
entry(NULL
/* instance */, -1 /* page_id */, kUrl
,
969 Referrer(), base::string16() /* title */,
970 ui::PAGE_TRANSITION_TYPED
,
971 false /* is_renderer_init */);
972 RenderFrameHostImpl
* host
= manager
->Navigate(entry
);
974 // We commit the pending RenderFrameHost immediately because the previous
975 // RenderFrameHost was not live. We test a case where it is live in
978 EXPECT_EQ(host
, manager
->current_frame_host());
979 EXPECT_FALSE(manager
->pending_frame_host());
981 // It's important that the site instance get set on the Web UI page as soon
982 // as the navigation starts, rather than lazily after it commits, so we don't
983 // try to re-use the SiteInstance/process for non Web UI things that may
984 // get loaded in between.
985 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
986 EXPECT_EQ(kUrl
, host
->GetSiteInstance()->GetSiteURL());
988 // The Web UI is committed immediately because the RenderViewHost has not been
989 // used yet. UpdateStateForNavigate() took the short cut path.
990 EXPECT_FALSE(manager
->pending_web_ui());
991 EXPECT_TRUE(manager
->web_ui());
994 manager
->DidNavigateFrame(host
);
996 host
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
999 // Tests that we can open a WebUI link in a new tab from a WebUI page and still
1000 // grant the correct bindings. http://crbug.com/189101.
1001 TEST_F(RenderFrameHostManagerTest
, WebUIInNewTab
) {
1002 set_should_create_webui(true);
1003 SiteInstance
* blank_instance
= SiteInstance::Create(browser_context());
1005 // Create a blank tab.
1006 scoped_ptr
<TestWebContents
> web_contents1(
1007 TestWebContents::Create(browser_context(), blank_instance
));
1008 RenderFrameHostManager
* manager1
=
1009 web_contents1
->GetRenderManagerForTesting();
1010 // Test the case that new RVH is considered live.
1011 manager1
->current_host()->CreateRenderView(
1012 base::string16(), -1, MSG_ROUTING_NONE
, -1, false);
1013 EXPECT_TRUE(manager1
->current_host()->IsRenderViewLive());
1014 EXPECT_TRUE(manager1
->current_frame_host()->IsRenderFrameLive());
1016 // Navigate to a WebUI page.
1017 const GURL
kUrl1("chrome://foo");
1018 NavigationEntryImpl
entry1(NULL
/* instance */, -1 /* page_id */, kUrl1
,
1019 Referrer(), base::string16() /* title */,
1020 ui::PAGE_TRANSITION_TYPED
,
1021 false /* is_renderer_init */);
1022 RenderFrameHostImpl
* host1
= manager1
->Navigate(entry1
);
1024 // We should have a pending navigation to the WebUI RenderViewHost.
1025 // It should already have bindings.
1026 EXPECT_EQ(host1
, manager1
->pending_frame_host());
1027 EXPECT_NE(host1
, manager1
->current_frame_host());
1029 host1
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1031 // Commit and ensure we still have bindings.
1032 manager1
->DidNavigateFrame(host1
);
1033 SiteInstance
* webui_instance
= host1
->GetSiteInstance();
1034 EXPECT_EQ(host1
, manager1
->current_frame_host());
1036 host1
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1038 // Now simulate clicking a link that opens in a new tab.
1039 scoped_ptr
<TestWebContents
> web_contents2(
1040 TestWebContents::Create(browser_context(), webui_instance
));
1041 RenderFrameHostManager
* manager2
=
1042 web_contents2
->GetRenderManagerForTesting();
1043 // Make sure the new RVH is considered live. This is usually done in
1044 // RenderWidgetHost::Init when opening a new tab from a link.
1045 manager2
->current_host()->CreateRenderView(
1046 base::string16(), -1, MSG_ROUTING_NONE
, -1, false);
1048 const GURL
kUrl2("chrome://foo/bar");
1049 NavigationEntryImpl
entry2(NULL
/* instance */, -1 /* page_id */, kUrl2
,
1050 Referrer(), base::string16() /* title */,
1051 ui::PAGE_TRANSITION_LINK
,
1052 true /* is_renderer_init */);
1053 RenderFrameHostImpl
* host2
= manager2
->Navigate(entry2
);
1055 // No cross-process transition happens because we are already in the right
1056 // SiteInstance. We should grant bindings immediately.
1057 EXPECT_EQ(host2
, manager2
->current_frame_host());
1059 host2
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1061 manager2
->DidNavigateFrame(host2
);
1064 // Tests that we don't end up in an inconsistent state if a page does a back and
1065 // then reload. http://crbug.com/51680
1066 TEST_F(RenderFrameHostManagerTest
, PageDoesBackAndReload
) {
1067 const GURL
kUrl1("http://www.google.com/");
1068 const GURL
kUrl2("http://www.evil-site.com/");
1070 // Navigate to a safe site, then an evil site.
1071 // This will switch RenderFrameHosts. We cannot assert that the first and
1072 // second RFHs are different, though, because the first one may be promptly
1074 contents()->NavigateAndCommit(kUrl1
);
1075 contents()->NavigateAndCommit(kUrl2
);
1076 TestRenderFrameHost
* evil_rfh
= contents()->GetMainFrame();
1078 // Now let's simulate the evil page calling history.back().
1079 contents()->OnGoToEntryAtOffset(-1);
1080 // We should have a new pending RFH.
1081 // Note that in this case, the navigation has not committed, so evil_rfh will
1082 // not be deleted yet.
1083 EXPECT_NE(evil_rfh
, contents()->GetPendingMainFrame());
1084 EXPECT_NE(evil_rfh
->GetRenderViewHost(),
1085 contents()->GetPendingMainFrame()->GetRenderViewHost());
1087 // Before that RFH has committed, the evil page reloads itself.
1088 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1091 params
.transition
= ui::PAGE_TRANSITION_CLIENT_REDIRECT
;
1092 params
.should_update_history
= false;
1093 params
.gesture
= NavigationGestureAuto
;
1094 params
.was_within_same_page
= false;
1095 params
.is_post
= false;
1096 params
.page_state
= PageState::CreateFromURL(kUrl2
);
1098 contents()->GetFrameTree()->root()->navigator()->DidNavigate(evil_rfh
,
1101 // That should have cancelled the pending RFH, and the evil RFH should be the
1103 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
1104 pending_render_view_host() == NULL
);
1105 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->pending_frame_host() ==
1108 contents()->GetRenderManagerForTesting()->current_frame_host());
1109 EXPECT_EQ(evil_rfh
->GetRenderViewHost(),
1110 contents()->GetRenderManagerForTesting()->current_host());
1112 // Also we should not have a pending navigation entry.
1113 EXPECT_TRUE(contents()->GetController().GetPendingEntry() == NULL
);
1114 NavigationEntry
* entry
= contents()->GetController().GetVisibleEntry();
1115 ASSERT_TRUE(entry
!= NULL
);
1116 EXPECT_EQ(kUrl2
, entry
->GetURL());
1119 // Ensure that we can go back and forward even if a SwapOut ACK isn't received.
1120 // See http://crbug.com/93427.
1121 TEST_F(RenderFrameHostManagerTest
, NavigateAfterMissingSwapOutACK
) {
1122 const GURL
kUrl1("http://www.google.com/");
1123 const GURL
kUrl2("http://www.chromium.org/");
1125 // Navigate to two pages.
1126 contents()->NavigateAndCommit(kUrl1
);
1127 TestRenderFrameHost
* rfh1
= main_test_rfh();
1129 // Keep active_frame_count nonzero so that no swapped out frames in
1130 // this SiteInstance get forcefully deleted.
1131 rfh1
->GetSiteInstance()->increment_active_frame_count();
1133 contents()->NavigateAndCommit(kUrl2
);
1134 TestRenderFrameHost
* rfh2
= main_test_rfh();
1135 rfh2
->GetSiteInstance()->increment_active_frame_count();
1137 // Now go back, but suppose the SwapOut_ACK isn't received. This shouldn't
1138 // happen, but we have seen it when going back quickly across many entries
1139 // (http://crbug.com/93427).
1140 contents()->GetController().GoBack();
1141 EXPECT_TRUE(rfh2
->is_waiting_for_beforeunload_ack());
1142 contents()->ProceedWithCrossSiteNavigation();
1143 EXPECT_FALSE(rfh2
->is_waiting_for_beforeunload_ack());
1145 // The back navigation commits.
1146 const NavigationEntry
* entry1
= contents()->GetController().GetPendingEntry();
1147 rfh1
->SendNavigate(entry1
->GetPageID(), entry1
->GetURL());
1148 EXPECT_TRUE(rfh2
->IsWaitingForUnloadACK());
1149 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh2
->rfh_state());
1151 // We should be able to navigate forward.
1152 contents()->GetController().GoForward();
1153 contents()->ProceedWithCrossSiteNavigation();
1154 const NavigationEntry
* entry2
= contents()->GetController().GetPendingEntry();
1155 rfh2
->SendNavigate(entry2
->GetPageID(), entry2
->GetURL());
1156 EXPECT_EQ(rfh2
, main_test_rfh());
1157 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
1158 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
1159 rfh1
->OnSwappedOut();
1160 EXPECT_TRUE(rfh1
->is_swapped_out());
1161 EXPECT_EQ(RenderFrameHostImpl::STATE_SWAPPED_OUT
, rfh1
->rfh_state());
1164 // Test that we create swapped out RFHs for the opener chain when navigating an
1165 // opened tab cross-process. This allows us to support certain cross-process
1166 // JavaScript calls (http://crbug.com/99202).
1167 TEST_F(RenderFrameHostManagerTest
, CreateSwappedOutOpenerRFHs
) {
1168 const GURL
kUrl1("http://www.google.com/");
1169 const GURL
kUrl2("http://www.chromium.org/");
1170 const GURL
kChromeUrl("chrome://foo");
1172 // Navigate to an initial URL.
1173 contents()->NavigateAndCommit(kUrl1
);
1174 RenderFrameHostManager
* manager
= contents()->GetRenderManagerForTesting();
1175 TestRenderFrameHost
* rfh1
= main_test_rfh();
1176 TestRenderViewHost
* rvh1
= test_rvh();
1178 // Create 2 new tabs and simulate them being the opener chain for the main
1179 // tab. They should be in the same SiteInstance.
1180 scoped_ptr
<TestWebContents
> opener1(
1181 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1182 RenderFrameHostManager
* opener1_manager
=
1183 opener1
->GetRenderManagerForTesting();
1184 contents()->SetOpener(opener1
.get());
1186 scoped_ptr
<TestWebContents
> opener2(
1187 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1188 RenderFrameHostManager
* opener2_manager
=
1189 opener2
->GetRenderManagerForTesting();
1190 opener1
->SetOpener(opener2
.get());
1192 // Navigate to a cross-site URL (different SiteInstance but same
1193 // BrowsingInstance).
1194 contents()->NavigateAndCommit(kUrl2
);
1195 TestRenderFrameHost
* rfh2
= main_test_rfh();
1196 TestRenderViewHost
* rvh2
= test_rvh();
1197 EXPECT_NE(rfh1
->GetSiteInstance(), rfh2
->GetSiteInstance());
1198 EXPECT_TRUE(rfh1
->GetSiteInstance()->IsRelatedSiteInstance(
1199 rfh2
->GetSiteInstance()));
1201 // Ensure rvh1 is placed on swapped out list of the current tab.
1202 EXPECT_TRUE(manager
->IsOnSwappedOutList(rfh1
));
1203 EXPECT_TRUE(manager
->IsRVHOnSwappedOutList(rvh1
));
1205 manager
->GetRenderFrameProxyHost(rfh1
->GetSiteInstance())
1206 ->render_frame_host());
1208 manager
->GetSwappedOutRenderViewHost(rvh1
->GetSiteInstance()));
1210 // Ensure a swapped out RFH and RFH is created in the first opener tab.
1211 RenderFrameProxyHost
* opener1_proxy
=
1212 opener1_manager
->GetRenderFrameProxyHost(rfh2
->GetSiteInstance());
1213 RenderFrameHostImpl
* opener1_rfh
= opener1_proxy
->render_frame_host();
1214 TestRenderViewHost
* opener1_rvh
= static_cast<TestRenderViewHost
*>(
1215 opener1_manager
->GetSwappedOutRenderViewHost(rvh2
->GetSiteInstance()));
1216 EXPECT_TRUE(opener1_manager
->IsOnSwappedOutList(opener1_rfh
));
1217 EXPECT_TRUE(opener1_manager
->IsRVHOnSwappedOutList(opener1_rvh
));
1218 EXPECT_TRUE(opener1_rfh
->is_swapped_out());
1219 EXPECT_FALSE(opener1_rvh
->is_active());
1221 // Ensure a swapped out RFH and RVH is created in the second opener tab.
1222 RenderFrameProxyHost
* opener2_proxy
=
1223 opener2_manager
->GetRenderFrameProxyHost(rfh2
->GetSiteInstance());
1224 RenderFrameHostImpl
* opener2_rfh
= opener2_proxy
->render_frame_host();
1225 TestRenderViewHost
* opener2_rvh
= static_cast<TestRenderViewHost
*>(
1226 opener2_manager
->GetSwappedOutRenderViewHost(rvh2
->GetSiteInstance()));
1227 EXPECT_TRUE(opener2_manager
->IsOnSwappedOutList(opener2_rfh
));
1228 EXPECT_TRUE(opener2_manager
->IsRVHOnSwappedOutList(opener2_rvh
));
1229 EXPECT_TRUE(opener2_rfh
->is_swapped_out());
1230 EXPECT_FALSE(opener2_rvh
->is_active());
1232 // Navigate to a cross-BrowsingInstance URL.
1233 contents()->NavigateAndCommit(kChromeUrl
);
1234 TestRenderFrameHost
* rfh3
= main_test_rfh();
1235 EXPECT_NE(rfh1
->GetSiteInstance(), rfh3
->GetSiteInstance());
1236 EXPECT_FALSE(rfh1
->GetSiteInstance()->IsRelatedSiteInstance(
1237 rfh3
->GetSiteInstance()));
1239 // No scripting is allowed across BrowsingInstances, so we should not create
1240 // swapped out RVHs for the opener chain in this case.
1241 EXPECT_FALSE(opener1_manager
->GetRenderFrameProxyHost(
1242 rfh3
->GetSiteInstance()));
1243 EXPECT_FALSE(opener1_manager
->GetSwappedOutRenderViewHost(
1244 rfh3
->GetSiteInstance()));
1245 EXPECT_FALSE(opener2_manager
->GetRenderFrameProxyHost(
1246 rfh3
->GetSiteInstance()));
1247 EXPECT_FALSE(opener2_manager
->GetSwappedOutRenderViewHost(
1248 rfh3
->GetSiteInstance()));
1251 // Test that a page can disown the opener of the WebContents.
1252 TEST_F(RenderFrameHostManagerTest
, DisownOpener
) {
1253 const GURL
kUrl1("http://www.google.com/");
1254 const GURL
kUrl2("http://www.chromium.org/");
1256 // Navigate to an initial URL.
1257 contents()->NavigateAndCommit(kUrl1
);
1258 TestRenderFrameHost
* rfh1
= main_test_rfh();
1260 // Create a new tab and simulate having it be the opener for the main tab.
1261 scoped_ptr
<TestWebContents
> opener1(
1262 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1263 contents()->SetOpener(opener1
.get());
1264 EXPECT_TRUE(contents()->HasOpener());
1266 // Navigate to a cross-site URL (different SiteInstance but same
1267 // BrowsingInstance).
1268 contents()->NavigateAndCommit(kUrl2
);
1269 TestRenderFrameHost
* rfh2
= main_test_rfh();
1270 EXPECT_NE(rfh1
->GetSiteInstance(), rfh2
->GetSiteInstance());
1272 // Disown the opener from rfh2.
1273 rfh2
->DidDisownOpener();
1275 // Ensure the opener is cleared.
1276 EXPECT_FALSE(contents()->HasOpener());
1279 // Test that a page can disown a same-site opener of the WebContents.
1280 TEST_F(RenderFrameHostManagerTest
, DisownSameSiteOpener
) {
1281 const GURL
kUrl1("http://www.google.com/");
1283 // Navigate to an initial URL.
1284 contents()->NavigateAndCommit(kUrl1
);
1285 TestRenderFrameHost
* rfh1
= main_test_rfh();
1287 // Create a new tab and simulate having it be the opener for the main tab.
1288 scoped_ptr
<TestWebContents
> opener1(
1289 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1290 contents()->SetOpener(opener1
.get());
1291 EXPECT_TRUE(contents()->HasOpener());
1293 // Disown the opener from rfh1.
1294 rfh1
->DidDisownOpener();
1296 // Ensure the opener is cleared even if it is in the same process.
1297 EXPECT_FALSE(contents()->HasOpener());
1300 // Test that a page can disown the opener just as a cross-process navigation is
1302 TEST_F(RenderFrameHostManagerTest
, DisownOpenerDuringNavigation
) {
1303 const GURL
kUrl1("http://www.google.com/");
1304 const GURL
kUrl2("http://www.chromium.org/");
1306 // Navigate to an initial URL.
1307 contents()->NavigateAndCommit(kUrl1
);
1308 TestRenderFrameHost
* rfh1
= main_test_rfh();
1310 // Create a new tab and simulate having it be the opener for the main tab.
1311 scoped_ptr
<TestWebContents
> opener1(
1312 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1313 contents()->SetOpener(opener1
.get());
1314 EXPECT_TRUE(contents()->HasOpener());
1316 // Navigate to a cross-site URL (different SiteInstance but same
1317 // BrowsingInstance).
1318 contents()->NavigateAndCommit(kUrl2
);
1319 TestRenderFrameHost
* rfh2
= main_test_rfh();
1320 EXPECT_NE(rfh1
->GetSiteInstance(), rfh2
->GetSiteInstance());
1322 // Start a back navigation so that rfh1 becomes the pending RFH.
1323 contents()->GetController().GoBack();
1324 contents()->ProceedWithCrossSiteNavigation();
1326 // Disown the opener from rfh2.
1327 rfh2
->DidDisownOpener();
1329 // Ensure the opener is cleared.
1330 EXPECT_FALSE(contents()->HasOpener());
1332 // The back navigation commits.
1333 const NavigationEntry
* entry1
= contents()->GetController().GetPendingEntry();
1334 rfh1
->SendNavigate(entry1
->GetPageID(), entry1
->GetURL());
1336 // Ensure the opener is still cleared.
1337 EXPECT_FALSE(contents()->HasOpener());
1340 // Test that a page can disown the opener just after a cross-process navigation
1342 TEST_F(RenderFrameHostManagerTest
, DisownOpenerAfterNavigation
) {
1343 const GURL
kUrl1("http://www.google.com/");
1344 const GURL
kUrl2("http://www.chromium.org/");
1346 // Navigate to an initial URL.
1347 contents()->NavigateAndCommit(kUrl1
);
1348 TestRenderFrameHost
* rfh1
= main_test_rfh();
1350 // Create a new tab and simulate having it be the opener for the main tab.
1351 scoped_ptr
<TestWebContents
> opener1(
1352 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1353 contents()->SetOpener(opener1
.get());
1354 EXPECT_TRUE(contents()->HasOpener());
1356 // Navigate to a cross-site URL (different SiteInstance but same
1357 // BrowsingInstance).
1358 contents()->NavigateAndCommit(kUrl2
);
1359 TestRenderFrameHost
* rfh2
= main_test_rfh();
1360 EXPECT_NE(rfh1
->GetSiteInstance(), rfh2
->GetSiteInstance());
1362 // Commit a back navigation before the DidDisownOpener message arrives.
1363 // rfh1 will be kept alive because of the opener tab.
1364 contents()->GetController().GoBack();
1365 contents()->ProceedWithCrossSiteNavigation();
1366 const NavigationEntry
* entry1
= contents()->GetController().GetPendingEntry();
1367 rfh1
->SendNavigate(entry1
->GetPageID(), entry1
->GetURL());
1369 // Disown the opener from rfh2.
1370 rfh2
->DidDisownOpener();
1371 EXPECT_FALSE(contents()->HasOpener());
1374 // Test that we clean up swapped out RenderViewHosts when a process hosting
1375 // those associated RenderViews crashes. http://crbug.com/258993
1376 TEST_F(RenderFrameHostManagerTest
, CleanUpSwappedOutRVHOnProcessCrash
) {
1377 const GURL
kUrl1("http://www.google.com/");
1378 const GURL
kUrl2("http://www.chromium.org/");
1380 // Navigate to an initial URL.
1381 contents()->NavigateAndCommit(kUrl1
);
1382 TestRenderViewHost
* rvh1
= test_rvh();
1384 // Create a new tab as an opener for the main tab.
1385 scoped_ptr
<TestWebContents
> opener1(
1386 TestWebContents::Create(browser_context(), rvh1
->GetSiteInstance()));
1387 RenderFrameHostManager
* opener1_manager
=
1388 opener1
->GetRenderManagerForTesting();
1389 contents()->SetOpener(opener1
.get());
1391 // Make sure the new opener RVH is considered live.
1392 opener1_manager
->current_host()->CreateRenderView(
1393 base::string16(), -1, MSG_ROUTING_NONE
, -1, false);
1394 EXPECT_TRUE(opener1_manager
->current_host()->IsRenderViewLive());
1395 EXPECT_TRUE(opener1_manager
->current_frame_host()->IsRenderFrameLive());
1397 // Use a cross-process navigation in the opener to swap out the old RVH.
1398 EXPECT_FALSE(opener1_manager
->GetSwappedOutRenderViewHost(
1399 rvh1
->GetSiteInstance()));
1400 opener1
->NavigateAndCommit(kUrl2
);
1401 EXPECT_TRUE(opener1_manager
->GetSwappedOutRenderViewHost(
1402 rvh1
->GetSiteInstance()));
1404 // Fake a process crash.
1405 RenderProcessHost::RendererClosedDetails
details(
1406 rvh1
->GetProcess()->GetHandle(),
1407 base::TERMINATION_STATUS_PROCESS_CRASHED
,
1409 NotificationService::current()->Notify(
1410 NOTIFICATION_RENDERER_PROCESS_CLOSED
,
1411 Source
<RenderProcessHost
>(rvh1
->GetProcess()),
1412 Details
<RenderProcessHost::RendererClosedDetails
>(&details
));
1413 rvh1
->set_render_view_created(false);
1415 // Ensure that the swapped out RenderViewHost has been deleted.
1416 EXPECT_FALSE(opener1_manager
->GetSwappedOutRenderViewHost(
1417 rvh1
->GetSiteInstance()));
1419 // Reload the initial tab. This should recreate the opener's swapped out RVH
1420 // in the original SiteInstance.
1421 contents()->GetController().Reload(true);
1422 EXPECT_EQ(opener1_manager
->GetSwappedOutRenderViewHost(
1423 rvh1
->GetSiteInstance())->GetRoutingID(),
1424 test_rvh()->opener_route_id());
1427 // Test that RenderViewHosts created for WebUI navigations are properly
1428 // granted WebUI bindings even if an unprivileged swapped out RenderViewHost
1429 // is in the same process (http://crbug.com/79918).
1430 TEST_F(RenderFrameHostManagerTest
, EnableWebUIWithSwappedOutOpener
) {
1431 set_should_create_webui(true);
1432 const GURL
kSettingsUrl("chrome://chrome/settings");
1433 const GURL
kPluginUrl("chrome://plugins");
1435 // Navigate to an initial WebUI URL.
1436 contents()->NavigateAndCommit(kSettingsUrl
);
1438 // Ensure the RVH has WebUI bindings.
1439 TestRenderViewHost
* rvh1
= test_rvh();
1440 EXPECT_TRUE(rvh1
->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1442 // Create a new tab and simulate it being the opener for the main
1443 // tab. It should be in the same SiteInstance.
1444 scoped_ptr
<TestWebContents
> opener1(
1445 TestWebContents::Create(browser_context(), rvh1
->GetSiteInstance()));
1446 RenderFrameHostManager
* opener1_manager
=
1447 opener1
->GetRenderManagerForTesting();
1448 contents()->SetOpener(opener1
.get());
1450 // Navigate to a different WebUI URL (different SiteInstance, same
1451 // BrowsingInstance).
1452 contents()->NavigateAndCommit(kPluginUrl
);
1453 TestRenderViewHost
* rvh2
= test_rvh();
1454 EXPECT_NE(rvh1
->GetSiteInstance(), rvh2
->GetSiteInstance());
1455 EXPECT_TRUE(rvh1
->GetSiteInstance()->IsRelatedSiteInstance(
1456 rvh2
->GetSiteInstance()));
1458 // Ensure a swapped out RFH and RVH is created in the first opener tab.
1459 RenderFrameProxyHost
* opener1_proxy
=
1460 opener1_manager
->GetRenderFrameProxyHost(rvh2
->GetSiteInstance());
1461 RenderFrameHostImpl
* opener1_rfh
= opener1_proxy
->render_frame_host();
1462 TestRenderViewHost
* opener1_rvh
= static_cast<TestRenderViewHost
*>(
1463 opener1_manager
->GetSwappedOutRenderViewHost(rvh2
->GetSiteInstance()));
1464 EXPECT_TRUE(opener1_manager
->IsOnSwappedOutList(opener1_rfh
));
1465 EXPECT_TRUE(opener1_manager
->IsRVHOnSwappedOutList(opener1_rvh
));
1466 EXPECT_TRUE(opener1_rfh
->is_swapped_out());
1467 EXPECT_FALSE(opener1_rvh
->is_active());
1469 // Ensure the new RVH has WebUI bindings.
1470 EXPECT_TRUE(rvh2
->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1473 // Test that we reuse the same guest SiteInstance if we navigate across sites.
1474 TEST_F(RenderFrameHostManagerTest
, NoSwapOnGuestNavigations
) {
1475 TestNotificationTracker notifications
;
1477 GURL
guest_url(std::string(kGuestScheme
).append("://abc123"));
1478 SiteInstance
* instance
=
1479 SiteInstance::CreateForURL(browser_context(), guest_url
);
1480 scoped_ptr
<TestWebContents
> web_contents(
1481 TestWebContents::Create(browser_context(), instance
));
1483 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
1485 RenderFrameHostImpl
* host
= NULL
;
1487 // 1) The first navigation. --------------------------
1488 const GURL
kUrl1("http://www.google.com/");
1489 NavigationEntryImpl
entry1(
1490 NULL
/* instance */, -1 /* page_id */, kUrl1
, Referrer(),
1491 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED
,
1492 false /* is_renderer_init */);
1493 host
= manager
->Navigate(entry1
);
1495 // The RenderFrameHost created in Init will be reused.
1496 EXPECT_TRUE(host
== manager
->current_frame_host());
1497 EXPECT_FALSE(manager
->pending_frame_host());
1498 EXPECT_EQ(manager
->current_frame_host()->GetSiteInstance(), instance
);
1501 manager
->DidNavigateFrame(host
);
1502 // Commit to SiteInstance should be delayed until RenderView commit.
1503 EXPECT_EQ(host
, manager
->current_frame_host());
1505 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
1507 // 2) Navigate to a different domain. -------------------------
1508 // Guests stay in the same process on navigation.
1509 const GURL
kUrl2("http://www.chromium.org");
1510 NavigationEntryImpl
entry2(
1511 NULL
/* instance */, -1 /* page_id */, kUrl2
,
1512 Referrer(kUrl1
, blink::WebReferrerPolicyDefault
),
1513 base::string16() /* title */, ui::PAGE_TRANSITION_LINK
,
1514 true /* is_renderer_init */);
1515 host
= manager
->Navigate(entry2
);
1517 // The RenderFrameHost created in Init will be reused.
1518 EXPECT_EQ(host
, manager
->current_frame_host());
1519 EXPECT_FALSE(manager
->pending_frame_host());
1522 manager
->DidNavigateFrame(host
);
1523 EXPECT_EQ(host
, manager
->current_frame_host());
1525 EXPECT_EQ(host
->GetSiteInstance(), instance
);
1528 // Test that we cancel a pending RVH if we close the tab while it's pending.
1529 // http://crbug.com/294697.
1530 TEST_F(RenderFrameHostManagerTest
, NavigateWithEarlyClose
) {
1531 TestNotificationTracker notifications
;
1533 SiteInstance
* instance
= SiteInstance::Create(browser_context());
1535 BeforeUnloadFiredWebContentsDelegate delegate
;
1536 scoped_ptr
<TestWebContents
> web_contents(
1537 TestWebContents::Create(browser_context(), instance
));
1538 web_contents
->SetDelegate(&delegate
);
1539 notifications
.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED
,
1540 Source
<WebContents
>(web_contents
.get()));
1542 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
1544 // 1) The first navigation. --------------------------
1545 const GURL
kUrl1("http://www.google.com/");
1546 NavigationEntryImpl
entry1(NULL
/* instance */, -1 /* page_id */, kUrl1
,
1547 Referrer(), base::string16() /* title */,
1548 ui::PAGE_TRANSITION_TYPED
,
1549 false /* is_renderer_init */);
1550 RenderFrameHostImpl
* host
= manager
->Navigate(entry1
);
1552 // The RenderFrameHost created in Init will be reused.
1553 EXPECT_EQ(host
, manager
->current_frame_host());
1554 EXPECT_FALSE(manager
->pending_frame_host());
1556 // We should observe a notification.
1558 notifications
.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED
));
1559 notifications
.Reset();
1562 manager
->DidNavigateFrame(host
);
1564 // Commit to SiteInstance should be delayed until RenderFrame commits.
1565 EXPECT_EQ(host
, manager
->current_frame_host());
1566 EXPECT_FALSE(host
->GetSiteInstance()->HasSite());
1567 host
->GetSiteInstance()->SetSite(kUrl1
);
1569 // 2) Cross-site navigate to next site. -------------------------
1570 const GURL
kUrl2("http://www.example.com");
1571 NavigationEntryImpl
entry2(
1572 NULL
/* instance */, -1 /* page_id */, kUrl2
, Referrer(),
1573 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED
,
1574 false /* is_renderer_init */);
1575 RenderFrameHostImpl
* host2
= manager
->Navigate(entry2
);
1577 // A new RenderFrameHost should be created.
1578 ASSERT_EQ(host2
, manager
->pending_frame_host());
1579 EXPECT_NE(host2
, host
);
1581 EXPECT_EQ(host
, manager
->current_frame_host());
1582 EXPECT_FALSE(manager
->current_frame_host()->is_swapped_out());
1583 EXPECT_EQ(host2
, manager
->pending_frame_host());
1585 // 3) Close the tab. -------------------------
1586 notifications
.ListenFor(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED
,
1587 Source
<RenderWidgetHost
>(host2
->render_view_host()));
1588 manager
->OnBeforeUnloadACK(false, true, base::TimeTicks());
1591 notifications
.Check1AndReset(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED
));
1592 EXPECT_FALSE(manager
->pending_frame_host());
1593 EXPECT_EQ(host
, manager
->current_frame_host());
1596 TEST_F(RenderFrameHostManagerTest
, CloseWithPendingWhileUnresponsive
) {
1597 const GURL
kUrl1("http://www.google.com/");
1598 const GURL
kUrl2("http://www.chromium.org/");
1600 CloseWebContentsDelegate close_delegate
;
1601 contents()->SetDelegate(&close_delegate
);
1603 // Navigate to the first page.
1604 contents()->NavigateAndCommit(kUrl1
);
1605 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1607 // Start to close the tab, but assume it's unresponsive.
1608 rfh1
->render_view_host()->ClosePage();
1609 EXPECT_TRUE(rfh1
->render_view_host()->is_waiting_for_close_ack());
1611 // Start a navigation to a new site.
1612 controller().LoadURL(
1613 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1614 EXPECT_TRUE(contents()->cross_navigation_pending());
1616 // Simulate the unresponsiveness timer. The tab should close.
1617 contents()->RendererUnresponsive(rfh1
->render_view_host());
1618 EXPECT_TRUE(close_delegate
.is_closed());
1621 // Tests that the RenderFrameHost is properly deleted when the SwapOutACK is
1622 // received. (SwapOut and the corresponding ACK always occur after commit.)
1623 // Also tests that an early SwapOutACK is properly ignored.
1624 TEST_F(RenderFrameHostManagerTest
, DeleteFrameAfterSwapOutACK
) {
1625 const GURL
kUrl1("http://www.google.com/");
1626 const GURL
kUrl2("http://www.chromium.org/");
1628 // Navigate to the first page.
1629 contents()->NavigateAndCommit(kUrl1
);
1630 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1631 RenderFrameHostDeletedObserver
rfh_deleted_observer(rfh1
);
1632 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1634 // Navigate to new site, simulating onbeforeunload approval.
1635 controller().LoadURL(
1636 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1637 base::TimeTicks now
= base::TimeTicks::Now();
1638 contents()->GetMainFrame()->OnMessageReceived(
1639 FrameHostMsg_BeforeUnload_ACK(0, true, now
, now
));
1640 EXPECT_TRUE(contents()->cross_navigation_pending());
1641 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1642 TestRenderFrameHost
* rfh2
= contents()->GetPendingMainFrame();
1644 // Simulate the swap out ack, unexpectedly early (before commit). It should
1646 rfh1
->OnSwappedOut();
1647 EXPECT_TRUE(contents()->cross_navigation_pending());
1648 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1650 // The new page commits.
1651 contents()->TestDidNavigate(rfh2
, 1, kUrl2
, ui::PAGE_TRANSITION_TYPED
);
1652 EXPECT_FALSE(contents()->cross_navigation_pending());
1653 EXPECT_EQ(rfh2
, contents()->GetMainFrame());
1654 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL
);
1655 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
1656 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
1658 rfh1
->frame_tree_node()->render_manager()->IsPendingDeletion(rfh1
));
1660 // Simulate the swap out ack.
1661 rfh1
->OnSwappedOut();
1663 // rfh1 should have been deleted.
1664 EXPECT_TRUE(rfh_deleted_observer
.deleted());
1668 // Tests that the RenderFrameHost is properly swapped out when the SwapOut ACK
1669 // is received. (SwapOut and the corresponding ACK always occur after commit.)
1670 TEST_F(RenderFrameHostManagerTest
, SwapOutFrameAfterSwapOutACK
) {
1671 const GURL
kUrl1("http://www.google.com/");
1672 const GURL
kUrl2("http://www.chromium.org/");
1674 // Navigate to the first page.
1675 contents()->NavigateAndCommit(kUrl1
);
1676 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1677 RenderFrameHostDeletedObserver
rfh_deleted_observer(rfh1
);
1678 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1680 // Increment the number of active frames in SiteInstanceImpl so that rfh1 is
1681 // not deleted on swap out.
1682 rfh1
->GetSiteInstance()->increment_active_frame_count();
1684 // Navigate to new site, simulating onbeforeunload approval.
1685 controller().LoadURL(
1686 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1687 base::TimeTicks now
= base::TimeTicks::Now();
1688 contents()->GetMainFrame()->OnMessageReceived(
1689 FrameHostMsg_BeforeUnload_ACK(0, true, now
, now
));
1690 EXPECT_TRUE(contents()->cross_navigation_pending());
1691 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1692 TestRenderFrameHost
* rfh2
= contents()->GetPendingMainFrame();
1694 // The new page commits.
1695 contents()->TestDidNavigate(rfh2
, 1, kUrl2
, ui::PAGE_TRANSITION_TYPED
);
1696 EXPECT_FALSE(contents()->cross_navigation_pending());
1697 EXPECT_EQ(rfh2
, contents()->GetMainFrame());
1698 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL
);
1699 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
1700 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
1702 // Simulate the swap out ack.
1703 rfh1
->OnSwappedOut();
1705 // rfh1 should be swapped out.
1706 EXPECT_FALSE(rfh_deleted_observer
.deleted());
1707 EXPECT_TRUE(rfh1
->is_swapped_out());
1710 // Test that the RenderViewHost is properly swapped out if a navigation in the
1711 // new renderer commits before sending the SwapOut message to the old renderer.
1712 // This simulates a cross-site navigation to a synchronously committing URL
1713 // (e.g., a data URL) and ensures it works properly.
1714 TEST_F(RenderFrameHostManagerTest
,
1715 CommitNewNavigationBeforeSendingSwapOut
) {
1716 const GURL
kUrl1("http://www.google.com/");
1717 const GURL
kUrl2("http://www.chromium.org/");
1719 // Navigate to the first page.
1720 contents()->NavigateAndCommit(kUrl1
);
1721 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1722 RenderFrameHostDeletedObserver
rfh_deleted_observer(rfh1
);
1723 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1725 // Increment the number of active frames in SiteInstanceImpl so that rfh1 is
1726 // not deleted on swap out.
1727 rfh1
->GetSiteInstance()->increment_active_frame_count();
1729 // Navigate to new site, simulating onbeforeunload approval.
1730 controller().LoadURL(
1731 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1732 base::TimeTicks now
= base::TimeTicks::Now();
1733 rfh1
->OnMessageReceived(
1734 FrameHostMsg_BeforeUnload_ACK(0, true, now
, now
));
1735 EXPECT_TRUE(contents()->cross_navigation_pending());
1736 TestRenderFrameHost
* rfh2
= contents()->GetPendingMainFrame();
1738 // The new page commits.
1739 contents()->TestDidNavigate(rfh2
, 1, kUrl2
, ui::PAGE_TRANSITION_TYPED
);
1740 EXPECT_FALSE(contents()->cross_navigation_pending());
1741 EXPECT_EQ(rfh2
, contents()->GetMainFrame());
1742 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL
);
1743 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
1744 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
1746 // Simulate the swap out ack.
1747 rfh1
->OnSwappedOut();
1749 // rfh1 should be swapped out.
1750 EXPECT_FALSE(rfh_deleted_observer
.deleted());
1751 EXPECT_TRUE(rfh1
->is_swapped_out());
1754 // Test that a RenderFrameHost is properly deleted or swapped out when a
1755 // cross-site navigation is cancelled.
1756 TEST_F(RenderFrameHostManagerTest
,
1757 CancelPendingProperlyDeletesOrSwaps
) {
1758 const GURL
kUrl1("http://www.google.com/");
1759 const GURL
kUrl2("http://www.chromium.org/");
1760 RenderFrameHostImpl
* pending_rfh
= NULL
;
1761 base::TimeTicks now
= base::TimeTicks::Now();
1763 // Navigate to the first page.
1764 contents()->NavigateAndCommit(kUrl1
);
1765 TestRenderFrameHost
* rfh1
= main_test_rfh();
1766 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1768 // Navigate to a new site, starting a cross-site navigation.
1769 controller().LoadURL(
1770 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1772 pending_rfh
= contents()->GetFrameTree()->root()->render_manager()
1773 ->pending_frame_host();
1774 RenderFrameHostDeletedObserver
rfh_deleted_observer(pending_rfh
);
1776 // Cancel the navigation by simulating a declined beforeunload dialog.
1777 contents()->GetMainFrame()->OnMessageReceived(
1778 FrameHostMsg_BeforeUnload_ACK(0, false, now
, now
));
1779 EXPECT_FALSE(contents()->cross_navigation_pending());
1781 // Since the pending RFH is the only one for the new SiteInstance, it should
1783 EXPECT_TRUE(rfh_deleted_observer
.deleted());
1786 // Start another cross-site navigation.
1787 controller().LoadURL(
1788 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1790 pending_rfh
= contents()->GetFrameTree()->root()->render_manager()
1791 ->pending_frame_host();
1792 RenderFrameHostDeletedObserver
rfh_deleted_observer(pending_rfh
);
1794 // Increment the number of active frames in the new SiteInstance, which will
1795 // cause the pending RFH to be swapped out instead of deleted.
1796 pending_rfh
->GetSiteInstance()->increment_active_frame_count();
1798 contents()->GetMainFrame()->OnMessageReceived(
1799 FrameHostMsg_BeforeUnload_ACK(0, false, now
, now
));
1800 EXPECT_FALSE(contents()->cross_navigation_pending());
1801 EXPECT_FALSE(rfh_deleted_observer
.deleted());
1805 } // namespace content