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/navigation_request.h"
14 #include "content/browser/frame_host/navigator.h"
15 #include "content/browser/frame_host/render_frame_host_manager.h"
16 #include "content/browser/frame_host/render_frame_proxy_host.h"
17 #include "content/browser/site_instance_impl.h"
18 #include "content/browser/webui/web_ui_controller_factory_registry.h"
19 #include "content/common/frame_messages.h"
20 #include "content/common/view_messages.h"
21 #include "content/public/browser/notification_details.h"
22 #include "content/public/browser/notification_service.h"
23 #include "content/public/browser/notification_source.h"
24 #include "content/public/browser/notification_types.h"
25 #include "content/public/browser/render_process_host.h"
26 #include "content/public/browser/render_widget_host_iterator.h"
27 #include "content/public/browser/web_contents_delegate.h"
28 #include "content/public/browser/web_contents_observer.h"
29 #include "content/public/browser/web_ui_controller.h"
30 #include "content/public/common/bindings_policy.h"
31 #include "content/public/common/content_switches.h"
32 #include "content/public/common/javascript_message_type.h"
33 #include "content/public/common/url_constants.h"
34 #include "content/public/common/url_utils.h"
35 #include "content/public/test/mock_render_process_host.h"
36 #include "content/public/test/test_notification_tracker.h"
37 #include "content/test/test_content_browser_client.h"
38 #include "content/test/test_content_client.h"
39 #include "content/test/test_render_frame_host.h"
40 #include "content/test/test_render_view_host.h"
41 #include "content/test/test_web_contents.h"
42 #include "net/base/load_flags.h"
43 #include "testing/gtest/include/gtest/gtest.h"
44 #include "ui/base/page_transition_types.h"
49 class RenderFrameHostManagerTestWebUIControllerFactory
50 : public WebUIControllerFactory
{
52 RenderFrameHostManagerTestWebUIControllerFactory()
53 : should_create_webui_(false) {
55 ~RenderFrameHostManagerTestWebUIControllerFactory() override
{}
57 void set_should_create_webui(bool should_create_webui
) {
58 should_create_webui_
= should_create_webui
;
61 // WebUIFactory implementation.
62 WebUIController
* CreateWebUIControllerForURL(WebUI
* web_ui
,
63 const GURL
& url
) const override
{
64 if (!(should_create_webui_
&& HasWebUIScheme(url
)))
66 return new WebUIController(web_ui
);
69 WebUI::TypeID
GetWebUIType(BrowserContext
* browser_context
,
70 const GURL
& url
) const override
{
71 return WebUI::kNoWebUI
;
74 bool UseWebUIForURL(BrowserContext
* browser_context
,
75 const GURL
& url
) const override
{
76 return HasWebUIScheme(url
);
79 bool UseWebUIBindingsForURL(BrowserContext
* browser_context
,
80 const GURL
& url
) const override
{
81 return HasWebUIScheme(url
);
85 bool should_create_webui_
;
87 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostManagerTestWebUIControllerFactory
);
90 class BeforeUnloadFiredWebContentsDelegate
: public WebContentsDelegate
{
92 BeforeUnloadFiredWebContentsDelegate() {}
93 ~BeforeUnloadFiredWebContentsDelegate() override
{}
95 void BeforeUnloadFired(WebContents
* web_contents
,
97 bool* proceed_to_fire_unload
) override
{
98 *proceed_to_fire_unload
= proceed
;
102 DISALLOW_COPY_AND_ASSIGN(BeforeUnloadFiredWebContentsDelegate
);
105 class CloseWebContentsDelegate
: public WebContentsDelegate
{
107 CloseWebContentsDelegate() : close_called_(false) {}
108 ~CloseWebContentsDelegate() override
{}
110 void CloseContents(WebContents
* web_contents
) override
{
111 close_called_
= true;
114 bool is_closed() { return close_called_
; }
117 DISALLOW_COPY_AND_ASSIGN(CloseWebContentsDelegate
);
122 // This observer keeps track of the last deleted RenderViewHost to avoid
123 // accessing it and causing use-after-free condition.
124 class RenderViewHostDeletedObserver
: public WebContentsObserver
{
126 RenderViewHostDeletedObserver(RenderViewHost
* rvh
)
127 : WebContentsObserver(WebContents::FromRenderViewHost(rvh
)),
128 process_id_(rvh
->GetProcess()->GetID()),
129 routing_id_(rvh
->GetRoutingID()),
133 void RenderViewDeleted(RenderViewHost
* render_view_host
) override
{
134 if (render_view_host
->GetProcess()->GetID() == process_id_
&&
135 render_view_host
->GetRoutingID() == routing_id_
) {
149 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDeletedObserver
);
152 // This observer keeps track of the last created RenderFrameHost to allow tests
153 // to ensure that no RenderFrameHost objects are created when not expected.
154 class RenderFrameHostCreatedObserver
: public WebContentsObserver
{
156 RenderFrameHostCreatedObserver(WebContents
* web_contents
)
157 : WebContentsObserver(web_contents
),
161 void RenderFrameCreated(RenderFrameHost
* render_frame_host
) override
{
172 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostCreatedObserver
);
175 // This observer keeps track of the last deleted RenderFrameHost to avoid
176 // accessing it and causing use-after-free condition.
177 class RenderFrameHostDeletedObserver
: public WebContentsObserver
{
179 RenderFrameHostDeletedObserver(RenderFrameHost
* rfh
)
180 : WebContentsObserver(WebContents::FromRenderFrameHost(rfh
)),
181 process_id_(rfh
->GetProcess()->GetID()),
182 routing_id_(rfh
->GetRoutingID()),
186 void RenderFrameDeleted(RenderFrameHost
* render_frame_host
) override
{
187 if (render_frame_host
->GetProcess()->GetID() == process_id_
&&
188 render_frame_host
->GetRoutingID() == routing_id_
) {
202 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostDeletedObserver
);
205 // This observer is used to check whether IPC messages are being filtered for
206 // swapped out RenderFrameHost objects. It observes the plugin crash and favicon
207 // update events, which the FilterMessagesWhileSwappedOut test simulates being
208 // sent. The test is successful if the event is not observed.
209 // See http://crbug.com/351815
210 class PluginFaviconMessageObserver
: public WebContentsObserver
{
212 PluginFaviconMessageObserver(WebContents
* web_contents
)
213 : WebContentsObserver(web_contents
),
214 plugin_crashed_(false),
215 favicon_received_(false) { }
217 void PluginCrashed(const base::FilePath
& plugin_path
,
218 base::ProcessId plugin_pid
) override
{
219 plugin_crashed_
= true;
222 void DidUpdateFaviconURL(const std::vector
<FaviconURL
>& candidates
) override
{
223 favicon_received_
= true;
226 bool plugin_crashed() {
227 return plugin_crashed_
;
230 bool favicon_received() {
231 return favicon_received_
;
235 bool plugin_crashed_
;
236 bool favicon_received_
;
238 DISALLOW_COPY_AND_ASSIGN(PluginFaviconMessageObserver
);
243 class RenderFrameHostManagerTest
: public RenderViewHostImplTestHarness
{
245 void SetUp() override
{
246 RenderViewHostImplTestHarness::SetUp();
247 WebUIControllerFactory::RegisterFactory(&factory_
);
250 void TearDown() override
{
251 RenderViewHostImplTestHarness::TearDown();
252 WebUIControllerFactory::UnregisterFactoryForTesting(&factory_
);
255 void set_should_create_webui(bool should_create_webui
) {
256 factory_
.set_should_create_webui(should_create_webui
);
259 void NavigateActiveAndCommit(const GURL
& url
) {
260 // Note: we navigate the active RenderFrameHost because previous navigations
261 // won't have committed yet, so NavigateAndCommit does the wrong thing
263 controller().LoadURL(
264 url
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
266 // Simulate the BeforeUnload_ACK that is received from the current renderer
267 // for a cross-site navigation.
268 // PlzNavigate: it is necessary to call PrepareForCommit before getting the
269 // main and the pending frame because when we are trying to navigate to a
270 // WebUI from a new tab, a RenderFrameHost is created to display it that is
271 // committed immediately (since it is a new tab). Therefore the main frame
272 // is replaced without a pending frame being created, and we don't get the
273 // right values for the RFH to navigate: we try to use the old one that has
274 // been deleted in the meantime.
275 contents()->GetMainFrame()->PrepareForCommit(url
);
277 TestRenderFrameHost
* old_rfh
= contents()->GetMainFrame();
278 TestRenderFrameHost
* active_rfh
= contents()->GetPendingMainFrame()
279 ? contents()->GetPendingMainFrame()
281 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, old_rfh
->rfh_state());
283 // Commit the navigation with a new page ID.
284 int32 max_page_id
= contents()->GetMaxPageIDForSiteInstance(
285 active_rfh
->GetSiteInstance());
287 // Use an observer to avoid accessing a deleted renderer later on when the
288 // state is being checked.
289 RenderFrameHostDeletedObserver
rfh_observer(old_rfh
);
290 RenderViewHostDeletedObserver
rvh_observer(old_rfh
->GetRenderViewHost());
291 active_rfh
->SendNavigate(max_page_id
+ 1, url
);
293 // Make sure that we start to run the unload handler at the time of commit.
294 bool expecting_rfh_shutdown
= false;
295 if (old_rfh
!= active_rfh
&& !rfh_observer
.deleted()) {
296 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
,
297 old_rfh
->rfh_state());
298 if (!old_rfh
->GetSiteInstance()->active_frame_count()) {
299 expecting_rfh_shutdown
= true;
301 old_rfh
->frame_tree_node()->render_manager()->IsPendingDeletion(
306 // Simulate the swap out ACK coming from the pending renderer. This should
307 // either shut down the old RFH or leave it in a swapped out state.
308 if (old_rfh
!= active_rfh
) {
309 old_rfh
->OnSwappedOut();
310 if (expecting_rfh_shutdown
) {
311 EXPECT_TRUE(rfh_observer
.deleted());
312 EXPECT_TRUE(rvh_observer
.deleted());
314 EXPECT_EQ(RenderFrameHostImpl::STATE_SWAPPED_OUT
,
315 old_rfh
->rfh_state());
318 EXPECT_EQ(active_rfh
, contents()->GetMainFrame());
319 EXPECT_EQ(NULL
, contents()->GetPendingMainFrame());
322 bool ShouldSwapProcesses(RenderFrameHostManager
* manager
,
323 const NavigationEntryImpl
* current_entry
,
324 const NavigationEntryImpl
* new_entry
) const {
326 BrowserContext
* browser_context
=
327 manager
->delegate_
->GetControllerForRenderManager().GetBrowserContext();
328 const GURL
& current_effective_url
= current_entry
?
329 SiteInstanceImpl::GetEffectiveURL(browser_context
,
330 current_entry
->GetURL()) :
331 manager
->render_frame_host_
->GetSiteInstance()->GetSiteURL();
332 bool current_is_view_source_mode
= current_entry
?
333 current_entry
->IsViewSourceMode() : new_entry
->IsViewSourceMode();
334 return manager
->ShouldSwapBrowsingInstancesForNavigation(
335 current_effective_url
,
336 current_is_view_source_mode
,
337 new_entry
->site_instance(),
338 SiteInstanceImpl::GetEffectiveURL(browser_context
, new_entry
->GetURL()),
339 new_entry
->IsViewSourceMode());
342 // Creates a test RenderFrameHost that's swapped out.
343 TestRenderFrameHost
* CreateSwappedOutRenderFrameHost() {
344 const GURL
kChromeURL("chrome://foo");
345 const GURL
kDestUrl("http://www.google.com/");
347 // Navigate our first tab to a chrome url and then to the destination.
348 NavigateActiveAndCommit(kChromeURL
);
349 TestRenderFrameHost
* ntp_rfh
= contents()->GetMainFrame();
351 // Navigate to a cross-site URL.
352 contents()->GetController().LoadURL(
353 kDestUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
354 contents()->GetMainFrame()->PrepareForCommit(kDestUrl
);
355 EXPECT_TRUE(contents()->cross_navigation_pending());
357 // Manually increase the number of active frames in the
358 // SiteInstance that ntp_rfh belongs to, to prevent it from being
359 // destroyed when it gets swapped out.
360 ntp_rfh
->GetSiteInstance()->increment_active_frame_count();
362 TestRenderFrameHost
* dest_rfh
= contents()->GetPendingMainFrame();
364 EXPECT_NE(ntp_rfh
, dest_rfh
);
366 // BeforeUnload finishes.
367 ntp_rfh
->SendBeforeUnloadACK(true);
369 dest_rfh
->SendNavigate(101, kDestUrl
);
370 ntp_rfh
->OnSwappedOut();
372 EXPECT_TRUE(ntp_rfh
->is_swapped_out());
376 // Returns the RenderFrameHost that should be used in the navigation to
378 RenderFrameHostImpl
* GetFrameHostForNavigation(
379 RenderFrameHostManager
* manager
,
380 const NavigationEntryImpl
& entry
) {
381 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
382 switches::kEnableBrowserSideNavigation
)) {
383 scoped_ptr
<NavigationRequest
> navigation_request
=
384 NavigationRequest::CreateBrowserInitiated(
385 manager
->frame_tree_node_
, entry
, FrameMsg_Navigate_Type::NORMAL
,
386 base::TimeTicks::Now());
387 return manager
->GetFrameHostForNavigation(*navigation_request
);
389 return manager
->Navigate(entry
);
392 // Returns the pending RenderFrameHost.
393 // PlzNavigate: returns the speculative RenderFrameHost.
394 RenderFrameHostImpl
* GetPendingFrameHost(
395 RenderFrameHostManager
* manager
) {
396 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
397 switches::kEnableBrowserSideNavigation
)) {
398 return manager
->speculative_render_frame_host_
.get();
400 return manager
->pending_frame_host();
404 RenderFrameHostManagerTestWebUIControllerFactory factory_
;
407 // Tests that when you navigate from a chrome:// url to another page, and
408 // then do that same thing in another tab, that the two resulting pages have
409 // different SiteInstances, BrowsingInstances, and RenderProcessHosts. This is
410 // a regression test for bug 9364.
411 TEST_F(RenderFrameHostManagerTest
, NewTabPageProcesses
) {
412 set_should_create_webui(true);
413 const GURL
kChromeUrl("chrome://foo");
414 const GURL
kDestUrl("http://www.google.com/");
416 // Navigate our first tab to the chrome url and then to the destination,
417 // ensuring we grant bindings to the chrome URL.
418 NavigateActiveAndCommit(kChromeUrl
);
419 EXPECT_TRUE(active_rvh()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
420 NavigateActiveAndCommit(kDestUrl
);
422 EXPECT_FALSE(contents()->GetPendingMainFrame());
424 // Make a second tab.
425 scoped_ptr
<TestWebContents
> contents2(
426 TestWebContents::Create(browser_context(), NULL
));
428 // Load the two URLs in the second tab. Note that the first navigation creates
429 // a RFH that's not pending (since there is no cross-site transition), so
430 // we use the committed one.
431 contents2
->GetController().LoadURL(
432 kChromeUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
433 contents2
->GetMainFrame()->PrepareForCommit(kChromeUrl
);
434 TestRenderFrameHost
* ntp_rfh2
= contents2
->GetMainFrame();
435 EXPECT_FALSE(contents2
->cross_navigation_pending());
436 ntp_rfh2
->SendNavigate(100, kChromeUrl
);
438 // The second one is the opposite, creating a cross-site transition and
439 // requiring a beforeunload ack.
440 contents2
->GetController().LoadURL(
441 kDestUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
442 contents2
->GetMainFrame()->PrepareForCommit(kDestUrl
);
443 EXPECT_TRUE(contents2
->cross_navigation_pending());
444 TestRenderFrameHost
* dest_rfh2
= contents2
->GetPendingMainFrame();
445 ASSERT_TRUE(dest_rfh2
);
447 dest_rfh2
->SendNavigate(101, kDestUrl
);
449 // The two RFH's should be different in every way.
450 EXPECT_NE(contents()->GetMainFrame()->GetProcess(), dest_rfh2
->GetProcess());
451 EXPECT_NE(contents()->GetMainFrame()->GetSiteInstance(),
452 dest_rfh2
->GetSiteInstance());
453 EXPECT_FALSE(dest_rfh2
->GetSiteInstance()->IsRelatedSiteInstance(
454 contents()->GetMainFrame()->GetSiteInstance()));
456 // Navigate both to the new tab page, and verify that they share a
457 // RenderProcessHost (not a SiteInstance).
458 NavigateActiveAndCommit(kChromeUrl
);
459 EXPECT_FALSE(contents()->GetPendingMainFrame());
461 contents2
->GetController().LoadURL(
462 kChromeUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
463 contents2
->GetMainFrame()->PrepareForCommit(kChromeUrl
);
464 contents2
->GetPendingMainFrame()->SendNavigate(102, kChromeUrl
);
466 EXPECT_NE(contents()->GetMainFrame()->GetSiteInstance(),
467 contents2
->GetMainFrame()->GetSiteInstance());
468 EXPECT_EQ(contents()->GetMainFrame()->GetSiteInstance()->GetProcess(),
469 contents2
->GetMainFrame()->GetSiteInstance()->GetProcess());
472 // Ensure that the browser ignores most IPC messages that arrive from a
473 // RenderViewHost that has been swapped out. We do not want to take
474 // action on requests from a non-active renderer. The main exception is
475 // for synchronous messages, which cannot be ignored without leaving the
476 // renderer in a stuck state. See http://crbug.com/93427.
477 TEST_F(RenderFrameHostManagerTest
, FilterMessagesWhileSwappedOut
) {
478 const GURL
kChromeURL("chrome://foo");
479 const GURL
kDestUrl("http://www.google.com/");
480 std::vector
<FaviconURL
> icons
;
482 // Navigate our first tab to a chrome url and then to the destination.
483 NavigateActiveAndCommit(kChromeURL
);
484 TestRenderFrameHost
* ntp_rfh
= contents()->GetMainFrame();
486 // Send an update favicon message and make sure it works.
487 const base::string16 ntp_title
= base::ASCIIToUTF16("NTP Title");
489 PluginFaviconMessageObserver
observer(contents());
490 EXPECT_TRUE(ntp_rfh
->GetRenderViewHost()->OnMessageReceived(
491 ViewHostMsg_UpdateFaviconURL(
492 ntp_rfh
->GetRenderViewHost()->GetRoutingID(), icons
)));
493 EXPECT_TRUE(observer
.favicon_received());
495 // Create one more frame in the same SiteInstance where ntp_rfh
496 // exists so that it doesn't get deleted on navigation to another
498 ntp_rfh
->GetSiteInstance()->increment_active_frame_count();
501 // Navigate to a cross-site URL.
502 NavigateActiveAndCommit(kDestUrl
);
503 TestRenderFrameHost
* dest_rfh
= contents()->GetMainFrame();
504 ASSERT_TRUE(dest_rfh
);
505 EXPECT_NE(ntp_rfh
, dest_rfh
);
507 // The new RVH should be able to update its favicon.
508 const base::string16 dest_title
= base::ASCIIToUTF16("Google");
510 PluginFaviconMessageObserver
observer(contents());
512 dest_rfh
->GetRenderViewHost()->OnMessageReceived(
513 ViewHostMsg_UpdateFaviconURL(
514 dest_rfh
->GetRenderViewHost()->GetRoutingID(), icons
)));
515 EXPECT_TRUE(observer
.favicon_received());
518 // The old renderer, being slow, now updates the favicon. It should be
519 // filtered out and not take effect.
520 EXPECT_TRUE(ntp_rfh
->is_swapped_out());
522 PluginFaviconMessageObserver
observer(contents());
524 ntp_rfh
->GetRenderViewHost()->OnMessageReceived(
525 ViewHostMsg_UpdateFaviconURL(
526 dest_rfh
->GetRenderViewHost()->GetRoutingID(), icons
)));
527 EXPECT_FALSE(observer
.favicon_received());
530 #if defined(ENABLE_PLUGINS)
531 // The same logic should apply to RenderFrameHosts as well and routing through
532 // swapped out RFH shouldn't be allowed. Use a PluginCrashObserver to check
533 // if the IPC message is allowed through or not.
535 PluginFaviconMessageObserver
observer(contents());
536 EXPECT_TRUE(ntp_rfh
->OnMessageReceived(
537 FrameHostMsg_PluginCrashed(
538 ntp_rfh
->GetRoutingID(), base::FilePath(), 0)));
539 EXPECT_FALSE(observer
.plugin_crashed());
543 // We cannot filter out synchronous IPC messages, because the renderer would
544 // be left waiting for a reply. We pick RunBeforeUnloadConfirm as an example
545 // that can run easily within a unit test, and that needs to receive a reply
546 // without showing an actual dialog.
547 MockRenderProcessHost
* ntp_process_host
=
548 static_cast<MockRenderProcessHost
*>(ntp_rfh
->GetProcess());
549 ntp_process_host
->sink().ClearMessages();
550 const base::string16 msg
= base::ASCIIToUTF16("Message");
552 base::string16 unused
;
553 FrameHostMsg_RunBeforeUnloadConfirm
before_unload_msg(
554 ntp_rfh
->GetRoutingID(), kChromeURL
, msg
, false, &result
, &unused
);
555 // Enable pumping for check in BrowserMessageFilter::CheckCanDispatchOnUI.
556 before_unload_msg
.EnableMessagePumping();
557 EXPECT_TRUE(ntp_rfh
->OnMessageReceived(before_unload_msg
));
558 EXPECT_TRUE(ntp_process_host
->sink().GetUniqueMessageMatching(IPC_REPLY_ID
));
560 // Also test RunJavaScriptMessage.
561 ntp_process_host
->sink().ClearMessages();
562 FrameHostMsg_RunJavaScriptMessage
js_msg(
563 ntp_rfh
->GetRoutingID(), msg
, msg
, kChromeURL
,
564 JAVASCRIPT_MESSAGE_TYPE_CONFIRM
, &result
, &unused
);
565 js_msg
.EnableMessagePumping();
566 EXPECT_TRUE(ntp_rfh
->OnMessageReceived(js_msg
));
567 EXPECT_TRUE(ntp_process_host
->sink().GetUniqueMessageMatching(IPC_REPLY_ID
));
570 // Ensure that frames aren't added to the frame tree, if the message is coming
571 // from a process different than the parent frame's current RenderFrameHost
572 // process. Otherwise it is possible to have collisions of routing ids, as they
573 // are scoped per process. See https://crbug.com/415059.
574 TEST_F(RenderFrameHostManagerTest
, DropCreateChildFrameWhileSwappedOut
) {
575 const GURL
kUrl1("http://foo.com");
576 const GURL
kUrl2("http://www.google.com/");
578 // Navigate to the first site.
579 NavigateActiveAndCommit(kUrl1
);
580 TestRenderFrameHost
* initial_rfh
= contents()->GetMainFrame();
582 RenderFrameHostCreatedObserver
observer(contents());
583 initial_rfh
->OnCreateChildFrame(
584 initial_rfh
->GetProcess()->GetNextRoutingID(), std::string(),
586 EXPECT_TRUE(observer
.created());
589 // Create one more frame in the same SiteInstance where initial_rfh
590 // exists so that initial_rfh doesn't get deleted on navigation to another
592 initial_rfh
->GetSiteInstance()->increment_active_frame_count();
594 // Navigate to a cross-site URL.
595 NavigateActiveAndCommit(kUrl2
);
596 EXPECT_TRUE(initial_rfh
->is_swapped_out());
598 TestRenderFrameHost
* dest_rfh
= contents()->GetMainFrame();
599 ASSERT_TRUE(dest_rfh
);
600 EXPECT_NE(initial_rfh
, dest_rfh
);
603 // Since the old RFH is now swapped out, it shouldn't process any messages
604 // to create child frames.
605 RenderFrameHostCreatedObserver
observer(contents());
606 initial_rfh
->OnCreateChildFrame(
607 initial_rfh
->GetProcess()->GetNextRoutingID(), std::string(),
609 EXPECT_FALSE(observer
.created());
613 TEST_F(RenderFrameHostManagerTest
, WhiteListSwapCompositorFrame
) {
614 TestRenderFrameHost
* swapped_out_rfh
= CreateSwappedOutRenderFrameHost();
615 TestRenderWidgetHostView
* swapped_out_rwhv
=
616 static_cast<TestRenderWidgetHostView
*>(
617 swapped_out_rfh
->GetRenderViewHost()->GetView());
618 EXPECT_FALSE(swapped_out_rwhv
->did_swap_compositor_frame());
620 MockRenderProcessHost
* process_host
=
621 static_cast<MockRenderProcessHost
*>(swapped_out_rfh
->GetProcess());
622 process_host
->sink().ClearMessages();
624 cc::CompositorFrame frame
;
625 ViewHostMsg_SwapCompositorFrame
msg(
626 rvh()->GetRoutingID(), 0, frame
, std::vector
<IPC::Message
>());
628 EXPECT_TRUE(swapped_out_rfh
->render_view_host()->OnMessageReceived(msg
));
629 EXPECT_TRUE(swapped_out_rwhv
->did_swap_compositor_frame());
632 // Test if RenderViewHost::GetRenderWidgetHosts() only returns active
634 TEST_F(RenderFrameHostManagerTest
, GetRenderWidgetHostsReturnsActiveViews
) {
635 TestRenderFrameHost
* swapped_out_rfh
= CreateSwappedOutRenderFrameHost();
636 EXPECT_TRUE(swapped_out_rfh
->is_swapped_out());
638 scoped_ptr
<RenderWidgetHostIterator
> widgets(
639 RenderWidgetHost::GetRenderWidgetHosts());
640 // We know that there is the only one active widget. Another view is
641 // now swapped out, so the swapped out view is not included in the
643 RenderWidgetHost
* widget
= widgets
->GetNextHost();
644 EXPECT_FALSE(widgets
->GetNextHost());
645 RenderViewHost
* rvh
= RenderViewHost::From(widget
);
646 EXPECT_TRUE(static_cast<RenderViewHostImpl
*>(rvh
)->is_active());
649 // Test if RenderViewHost::GetRenderWidgetHosts() returns a subset of
650 // RenderViewHostImpl::GetAllRenderWidgetHosts().
651 // RenderViewHost::GetRenderWidgetHosts() returns only active widgets, but
652 // RenderViewHostImpl::GetAllRenderWidgetHosts() returns everything
653 // including swapped out ones.
654 TEST_F(RenderFrameHostManagerTest
,
655 GetRenderWidgetHostsWithinGetAllRenderWidgetHosts
) {
656 TestRenderFrameHost
* swapped_out_rfh
= CreateSwappedOutRenderFrameHost();
657 EXPECT_TRUE(swapped_out_rfh
->is_swapped_out());
659 scoped_ptr
<RenderWidgetHostIterator
> widgets(
660 RenderWidgetHost::GetRenderWidgetHosts());
662 while (RenderWidgetHost
* w
= widgets
->GetNextHost()) {
664 scoped_ptr
<RenderWidgetHostIterator
> all_widgets(
665 RenderWidgetHostImpl::GetAllRenderWidgetHosts());
666 while (RenderWidgetHost
* widget
= all_widgets
->GetNextHost()) {
676 // Test if SiteInstanceImpl::active_frame_count() is correctly updated
677 // as frames in a SiteInstance get swapped out and in.
678 TEST_F(RenderFrameHostManagerTest
, ActiveFrameCountWhileSwappingInAndOut
) {
679 const GURL
kUrl1("http://www.google.com/");
680 const GURL
kUrl2("http://www.chromium.org/");
682 // Navigate to an initial URL.
683 contents()->NavigateAndCommit(kUrl1
);
684 TestRenderFrameHost
* rfh1
= main_test_rfh();
686 SiteInstanceImpl
* instance1
= rfh1
->GetSiteInstance();
687 EXPECT_EQ(instance1
->active_frame_count(), 1U);
689 // Create 2 new tabs and simulate them being the opener chain for the main
690 // tab. They should be in the same SiteInstance.
691 scoped_ptr
<TestWebContents
> opener1(
692 TestWebContents::Create(browser_context(), instance1
));
693 contents()->SetOpener(opener1
.get());
695 scoped_ptr
<TestWebContents
> opener2(
696 TestWebContents::Create(browser_context(), instance1
));
697 opener1
->SetOpener(opener2
.get());
699 EXPECT_EQ(instance1
->active_frame_count(), 3U);
701 // Navigate to a cross-site URL (different SiteInstance but same
702 // BrowsingInstance).
703 contents()->NavigateAndCommit(kUrl2
);
704 TestRenderFrameHost
* rfh2
= main_test_rfh();
705 SiteInstanceImpl
* instance2
= rfh2
->GetSiteInstance();
707 // rvh2 is on chromium.org which is different from google.com on
708 // which other tabs are.
709 EXPECT_EQ(instance2
->active_frame_count(), 1U);
711 // There are two active views on google.com now.
712 EXPECT_EQ(instance1
->active_frame_count(), 2U);
714 // Navigate to the original origin (google.com).
715 contents()->NavigateAndCommit(kUrl1
);
717 EXPECT_EQ(instance1
->active_frame_count(), 3U);
720 // This deletes a WebContents when the given RVH is deleted. This is
721 // only for testing whether deleting an RVH does not cause any UaF in
722 // other parts of the system. For now, this class is only used for the
723 // next test cases to detect the bug mentioned at
724 // http://crbug.com/259859.
725 class RenderViewHostDestroyer
: public WebContentsObserver
{
727 RenderViewHostDestroyer(RenderViewHost
* render_view_host
,
728 WebContents
* web_contents
)
729 : WebContentsObserver(WebContents::FromRenderViewHost(render_view_host
)),
730 render_view_host_(render_view_host
),
731 web_contents_(web_contents
) {}
733 void RenderViewDeleted(RenderViewHost
* render_view_host
) override
{
734 if (render_view_host
== render_view_host_
)
735 delete web_contents_
;
739 RenderViewHost
* render_view_host_
;
740 WebContents
* web_contents_
;
742 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestroyer
);
745 // Test if ShutdownRenderViewHostsInSiteInstance() does not touch any
746 // RenderWidget that has been freed while deleting a RenderViewHost in
747 // a previous iteration. This is a regression test for
748 // http://crbug.com/259859.
749 TEST_F(RenderFrameHostManagerTest
,
750 DetectUseAfterFreeInShutdownRenderViewHostsInSiteInstance
) {
751 const GURL
kChromeURL("chrome://newtab");
752 const GURL
kUrl1("http://www.google.com");
753 const GURL
kUrl2("http://www.chromium.org");
755 // Navigate our first tab to a chrome url and then to the destination.
756 NavigateActiveAndCommit(kChromeURL
);
757 TestRenderFrameHost
* ntp_rfh
= contents()->GetMainFrame();
759 // Create one more tab and navigate to kUrl1. web_contents is not
760 // wrapped as scoped_ptr since it intentionally deleted by destroyer
761 // below as part of this test.
762 TestWebContents
* web_contents
=
763 TestWebContents::Create(browser_context(), ntp_rfh
->GetSiteInstance());
764 web_contents
->NavigateAndCommit(kUrl1
);
765 RenderViewHostDestroyer
destroyer(ntp_rfh
->GetRenderViewHost(),
768 // This causes the first tab to navigate to kUrl2, which destroys
769 // the ntp_rfh in ShutdownRenderViewHostsInSiteInstance(). When
770 // ntp_rfh is destroyed, it also destroys the RVHs in web_contents
771 // too. This can test whether
772 // SiteInstanceImpl::ShutdownRenderViewHostsInSiteInstance() can
773 // touch any object freed in this way or not while iterating through
775 contents()->NavigateAndCommit(kUrl2
);
778 // When there is an error with the specified page, renderer exits view-source
779 // mode. See WebFrameImpl::DidFail(). We check by this test that
780 // EnableViewSourceMode message is sent on every navigation regardless
781 // RenderView is being newly created or reused.
782 TEST_F(RenderFrameHostManagerTest
, AlwaysSendEnableViewSourceMode
) {
783 const GURL
kChromeUrl("chrome://foo");
784 const GURL
kUrl("view-source:http://foo");
786 // We have to navigate to some page at first since without this, the first
787 // navigation will reuse the SiteInstance created by Init(), and the second
788 // one will create a new SiteInstance. Because current_instance and
789 // new_instance will be different, a new RenderViewHost will be created for
790 // the second navigation. We have to avoid this in order to exercise the
791 // target code patch.
792 NavigateActiveAndCommit(kChromeUrl
);
795 controller().LoadURL(
796 kUrl
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
797 // Simulate response from RenderFrame for DispatchBeforeUnload.
798 contents()->GetMainFrame()->PrepareForCommit(kUrl
);
799 ASSERT_TRUE(contents()->GetPendingMainFrame())
800 << "Expected new pending RenderFrameHost to be created.";
801 RenderFrameHost
* last_rfh
= contents()->GetPendingMainFrame();
803 contents()->GetMaxPageIDForSiteInstance(last_rfh
->GetSiteInstance()) + 1;
804 contents()->GetPendingMainFrame()->SendNavigate(new_id
, kUrl
);
805 EXPECT_EQ(controller().GetLastCommittedEntryIndex(), 1);
806 ASSERT_TRUE(controller().GetLastCommittedEntry());
807 EXPECT_TRUE(kUrl
== controller().GetLastCommittedEntry()->GetURL());
808 EXPECT_FALSE(controller().GetPendingEntry());
809 // Because we're using TestWebContents and TestRenderViewHost in this
810 // unittest, no one calls WebContentsImpl::RenderViewCreated(). So, we see no
811 // EnableViewSourceMode message, here.
813 // Clear queued messages before load.
814 process()->sink().ClearMessages();
816 controller().LoadURL(
817 kUrl
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
818 contents()->GetMainFrame()->PrepareForCommit(kUrl
);
819 // The same RenderViewHost should be reused.
820 EXPECT_FALSE(contents()->GetPendingMainFrame());
821 EXPECT_TRUE(last_rfh
== contents()->GetMainFrame());
822 // Navigate using the returned page_id.
823 contents()->GetMainFrame()->SendNavigate(new_id
, kUrl
);
824 EXPECT_EQ(controller().GetLastCommittedEntryIndex(), 1);
825 EXPECT_FALSE(controller().GetPendingEntry());
826 // New message should be sent out to make sure to enter view-source mode.
827 EXPECT_TRUE(process()->sink().GetUniqueMessageMatching(
828 ViewMsg_EnableViewSourceMode::ID
));
831 // Tests the Init function by checking the initial RenderViewHost.
832 TEST_F(RenderFrameHostManagerTest
, Init
) {
833 // Using TestBrowserContext.
834 SiteInstanceImpl
* instance
=
835 static_cast<SiteInstanceImpl
*>(SiteInstance::Create(browser_context()));
836 EXPECT_FALSE(instance
->HasSite());
838 scoped_ptr
<TestWebContents
> web_contents(
839 TestWebContents::Create(browser_context(), instance
));
841 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
842 RenderViewHostImpl
* rvh
= manager
->current_host();
843 RenderFrameHostImpl
* rfh
= manager
->current_frame_host();
846 EXPECT_EQ(rvh
, rfh
->render_view_host());
847 EXPECT_EQ(instance
, rvh
->GetSiteInstance());
848 EXPECT_EQ(web_contents
.get(), rvh
->GetDelegate());
849 EXPECT_EQ(web_contents
.get(), rfh
->delegate());
850 EXPECT_TRUE(manager
->GetRenderWidgetHostView());
851 EXPECT_FALSE(manager
->pending_render_view_host());
854 // Tests the Navigate function. We navigate three sites consecutively and check
855 // how the pending/committed RenderViewHost are modified.
856 TEST_F(RenderFrameHostManagerTest
, Navigate
) {
857 TestNotificationTracker notifications
;
859 SiteInstance
* instance
= SiteInstance::Create(browser_context());
861 scoped_ptr
<TestWebContents
> web_contents(
862 TestWebContents::Create(browser_context(), instance
));
863 notifications
.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED
,
864 Source
<WebContents
>(web_contents
.get()));
866 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
867 RenderFrameHostImpl
* host
= NULL
;
869 // 1) The first navigation. --------------------------
870 const GURL
kUrl1("http://www.google.com/");
871 NavigationEntryImpl
entry1(
872 NULL
/* instance */, -1 /* page_id */, kUrl1
, Referrer(),
873 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED
,
874 false /* is_renderer_init */);
875 host
= GetFrameHostForNavigation(manager
, entry1
);
877 // The RenderFrameHost created in Init will be reused.
878 EXPECT_TRUE(host
== manager
->current_frame_host());
879 EXPECT_FALSE(GetPendingFrameHost(manager
));
882 manager
->DidNavigateFrame(host
, true);
883 // Commit to SiteInstance should be delayed until RenderFrame commit.
884 EXPECT_TRUE(host
== manager
->current_frame_host());
886 EXPECT_FALSE(host
->GetSiteInstance()->HasSite());
887 host
->GetSiteInstance()->SetSite(kUrl1
);
889 // 2) Navigate to next site. -------------------------
890 const GURL
kUrl2("http://www.google.com/foo");
891 NavigationEntryImpl
entry2(
892 NULL
/* instance */, -1 /* page_id */, kUrl2
,
893 Referrer(kUrl1
, blink::WebReferrerPolicyDefault
),
894 base::string16() /* title */, ui::PAGE_TRANSITION_LINK
,
895 true /* is_renderer_init */);
896 host
= GetFrameHostForNavigation(manager
, entry2
);
898 // The RenderFrameHost created in Init will be reused.
899 EXPECT_TRUE(host
== manager
->current_frame_host());
900 EXPECT_FALSE(GetPendingFrameHost(manager
));
903 manager
->DidNavigateFrame(host
, true);
904 EXPECT_TRUE(host
== manager
->current_frame_host());
906 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
908 // 3) Cross-site navigate to next site. --------------
909 const GURL
kUrl3("http://webkit.org/");
910 NavigationEntryImpl
entry3(
911 NULL
/* instance */, -1 /* page_id */, kUrl3
,
912 Referrer(kUrl2
, blink::WebReferrerPolicyDefault
),
913 base::string16() /* title */, ui::PAGE_TRANSITION_LINK
,
914 false /* is_renderer_init */);
915 host
= GetFrameHostForNavigation(manager
, entry3
);
917 // A new RenderFrameHost should be created.
918 EXPECT_TRUE(GetPendingFrameHost(manager
));
919 ASSERT_EQ(host
, GetPendingFrameHost(manager
));
921 notifications
.Reset();
924 manager
->DidNavigateFrame(GetPendingFrameHost(manager
), true);
925 EXPECT_TRUE(host
== manager
->current_frame_host());
927 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
928 // Check the pending RenderFrameHost has been committed.
929 EXPECT_FALSE(GetPendingFrameHost(manager
));
931 // We should observe a notification.
933 notifications
.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED
));
936 // Tests WebUI creation.
937 TEST_F(RenderFrameHostManagerTest
, WebUI
) {
938 set_should_create_webui(true);
939 SiteInstance
* instance
= SiteInstance::Create(browser_context());
941 scoped_ptr
<TestWebContents
> web_contents(
942 TestWebContents::Create(browser_context(), instance
));
943 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
944 RenderFrameHostImpl
* initial_rfh
= manager
->current_frame_host();
946 EXPECT_FALSE(manager
->current_host()->IsRenderViewLive());
947 EXPECT_FALSE(manager
->web_ui());
948 EXPECT_TRUE(initial_rfh
);
950 const GURL
kUrl("chrome://foo");
951 NavigationEntryImpl
entry(NULL
/* instance */, -1 /* page_id */, kUrl
,
952 Referrer(), base::string16() /* title */,
953 ui::PAGE_TRANSITION_TYPED
,
954 false /* is_renderer_init */);
955 RenderFrameHostImpl
* host
= GetFrameHostForNavigation(manager
, entry
);
957 // We commit the pending RenderFrameHost immediately because the previous
958 // RenderFrameHost was not live. We test a case where it is live in
961 EXPECT_NE(initial_rfh
, host
);
962 EXPECT_EQ(host
, manager
->current_frame_host());
963 EXPECT_FALSE(GetPendingFrameHost(manager
));
965 // It's important that the site instance get set on the Web UI page as soon
966 // as the navigation starts, rather than lazily after it commits, so we don't
967 // try to re-use the SiteInstance/process for non Web UI things that may
968 // get loaded in between.
969 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
970 EXPECT_EQ(kUrl
, host
->GetSiteInstance()->GetSiteURL());
972 // The Web UI is committed immediately because the RenderViewHost has not been
973 // used yet. UpdateStateForNavigate() took the short cut path.
974 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
975 switches::kEnableBrowserSideNavigation
)) {
976 EXPECT_FALSE(manager
->speculative_web_ui());
978 EXPECT_FALSE(manager
->pending_web_ui());
980 EXPECT_TRUE(manager
->web_ui());
983 manager
->DidNavigateFrame(host
, true);
985 host
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
988 // Tests that we can open a WebUI link in a new tab from a WebUI page and still
989 // grant the correct bindings. http://crbug.com/189101.
990 TEST_F(RenderFrameHostManagerTest
, WebUIInNewTab
) {
991 set_should_create_webui(true);
992 SiteInstance
* blank_instance
= SiteInstance::Create(browser_context());
994 // Create a blank tab.
995 scoped_ptr
<TestWebContents
> web_contents1(
996 TestWebContents::Create(browser_context(), blank_instance
));
997 RenderFrameHostManager
* manager1
=
998 web_contents1
->GetRenderManagerForTesting();
999 // Test the case that new RVH is considered live.
1000 manager1
->current_host()->CreateRenderView(
1001 base::string16(), -1, MSG_ROUTING_NONE
, -1, false);
1002 EXPECT_TRUE(manager1
->current_host()->IsRenderViewLive());
1003 EXPECT_TRUE(manager1
->current_frame_host()->IsRenderFrameLive());
1005 // Navigate to a WebUI page.
1006 const GURL
kUrl1("chrome://foo");
1007 NavigationEntryImpl
entry1(NULL
/* instance */, -1 /* page_id */, kUrl1
,
1008 Referrer(), base::string16() /* title */,
1009 ui::PAGE_TRANSITION_TYPED
,
1010 false /* is_renderer_init */);
1011 RenderFrameHostImpl
* host1
= GetFrameHostForNavigation(manager1
, entry1
);
1013 // We should have a pending navigation to the WebUI RenderViewHost.
1014 // It should already have bindings.
1015 EXPECT_EQ(host1
, GetPendingFrameHost(manager1
));
1016 EXPECT_NE(host1
, manager1
->current_frame_host());
1018 host1
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1020 // Commit and ensure we still have bindings.
1021 manager1
->DidNavigateFrame(host1
, true);
1022 SiteInstance
* webui_instance
= host1
->GetSiteInstance();
1023 EXPECT_EQ(host1
, manager1
->current_frame_host());
1025 host1
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1027 // Now simulate clicking a link that opens in a new tab.
1028 scoped_ptr
<TestWebContents
> web_contents2(
1029 TestWebContents::Create(browser_context(), webui_instance
));
1030 RenderFrameHostManager
* manager2
=
1031 web_contents2
->GetRenderManagerForTesting();
1032 // Make sure the new RVH is considered live. This is usually done in
1033 // RenderWidgetHost::Init when opening a new tab from a link.
1034 manager2
->current_host()->CreateRenderView(
1035 base::string16(), -1, MSG_ROUTING_NONE
, -1, false);
1036 EXPECT_TRUE(manager2
->current_host()->IsRenderViewLive());
1038 const GURL
kUrl2("chrome://foo/bar");
1039 NavigationEntryImpl
entry2(NULL
/* instance */, -1 /* page_id */, kUrl2
,
1040 Referrer(), base::string16() /* title */,
1041 ui::PAGE_TRANSITION_LINK
,
1042 true /* is_renderer_init */);
1043 RenderFrameHostImpl
* host2
= GetFrameHostForNavigation(manager2
, entry2
);
1045 // No cross-process transition happens because we are already in the right
1046 // SiteInstance. We should grant bindings immediately.
1047 EXPECT_EQ(host2
, manager2
->current_frame_host());
1048 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1049 switches::kEnableBrowserSideNavigation
)) {
1050 EXPECT_TRUE(manager2
->speculative_web_ui());
1052 EXPECT_TRUE(manager2
->pending_web_ui());
1055 host2
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1057 manager2
->DidNavigateFrame(host2
, true);
1060 // Tests that we don't end up in an inconsistent state if a page does a back and
1061 // then reload. http://crbug.com/51680
1062 // Also tests that only user-gesture navigations can interrupt cross-process
1063 // navigations. http://crbug.com/75195
1064 TEST_F(RenderFrameHostManagerTest
, PageDoesBackAndReload
) {
1065 const GURL
kUrl1("http://www.google.com/");
1066 const GURL
kUrl2("http://www.evil-site.com/");
1068 // Navigate to a safe site, then an evil site.
1069 // This will switch RenderFrameHosts. We cannot assert that the first and
1070 // second RFHs are different, though, because the first one may be promptly
1072 contents()->NavigateAndCommit(kUrl1
);
1073 contents()->NavigateAndCommit(kUrl2
);
1074 TestRenderFrameHost
* evil_rfh
= contents()->GetMainFrame();
1076 // Now let's simulate the evil page calling history.back().
1077 contents()->OnGoToEntryAtOffset(-1);
1078 contents()->GetMainFrame()->PrepareForCommit(kUrl1
);
1079 // We should have a new pending RFH.
1080 // Note that in this case, the navigation has not committed, so evil_rfh will
1081 // not be deleted yet.
1082 EXPECT_NE(evil_rfh
, contents()->GetPendingMainFrame());
1083 EXPECT_NE(evil_rfh
->GetRenderViewHost(),
1084 contents()->GetPendingMainFrame()->GetRenderViewHost());
1086 // Before that RFH has committed, the evil page reloads itself.
1087 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1090 params
.transition
= ui::PAGE_TRANSITION_CLIENT_REDIRECT
;
1091 params
.should_update_history
= false;
1092 params
.gesture
= NavigationGestureAuto
;
1093 params
.was_within_same_page
= false;
1094 params
.is_post
= false;
1095 params
.page_state
= PageState::CreateFromURL(kUrl2
);
1097 contents()->GetFrameTree()->root()->navigator()->DidNavigate(evil_rfh
,
1100 // That should NOT have cancelled the pending RFH, because the reload did
1101 // not have a user gesture. Thus, the pending back navigation will still
1102 // eventually commit.
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());
1118 // Now do the same but as a user gesture.
1119 params
.gesture
= NavigationGestureUser
;
1120 contents()->GetFrameTree()->root()->navigator()->DidNavigate(evil_rfh
,
1123 // User navigation should have cancelled the pending RFH.
1124 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
1125 pending_render_view_host() == NULL
);
1126 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->pending_frame_host() ==
1129 contents()->GetRenderManagerForTesting()->current_frame_host());
1130 EXPECT_EQ(evil_rfh
->GetRenderViewHost(),
1131 contents()->GetRenderManagerForTesting()->current_host());
1133 // Also we should not have a pending navigation entry.
1134 EXPECT_TRUE(contents()->GetController().GetPendingEntry() == NULL
);
1135 entry
= contents()->GetController().GetVisibleEntry();
1136 ASSERT_TRUE(entry
!= NULL
);
1137 EXPECT_EQ(kUrl2
, entry
->GetURL());
1140 // Ensure that we can go back and forward even if a SwapOut ACK isn't received.
1141 // See http://crbug.com/93427.
1142 TEST_F(RenderFrameHostManagerTest
, NavigateAfterMissingSwapOutACK
) {
1143 const GURL
kUrl1("http://www.google.com/");
1144 const GURL
kUrl2("http://www.chromium.org/");
1146 // Navigate to two pages.
1147 contents()->NavigateAndCommit(kUrl1
);
1148 TestRenderFrameHost
* rfh1
= main_test_rfh();
1150 // Keep active_frame_count nonzero so that no swapped out frames in
1151 // this SiteInstance get forcefully deleted.
1152 rfh1
->GetSiteInstance()->increment_active_frame_count();
1154 contents()->NavigateAndCommit(kUrl2
);
1155 TestRenderFrameHost
* rfh2
= main_test_rfh();
1156 rfh2
->GetSiteInstance()->increment_active_frame_count();
1158 // Now go back, but suppose the SwapOut_ACK isn't received. This shouldn't
1159 // happen, but we have seen it when going back quickly across many entries
1160 // (http://crbug.com/93427).
1161 contents()->GetController().GoBack();
1162 EXPECT_TRUE(rfh2
->IsWaitingForBeforeUnloadACK());
1163 contents()->GetMainFrame()->PrepareForCommit(kUrl1
);
1164 EXPECT_FALSE(rfh2
->IsWaitingForBeforeUnloadACK());
1166 // The back navigation commits.
1167 const NavigationEntry
* entry1
= contents()->GetController().GetPendingEntry();
1168 rfh1
->SendNavigate(entry1
->GetPageID(), entry1
->GetURL());
1169 EXPECT_TRUE(rfh2
->IsWaitingForUnloadACK());
1170 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh2
->rfh_state());
1172 // We should be able to navigate forward.
1173 contents()->GetController().GoForward();
1174 contents()->GetMainFrame()->PrepareForCommit(kUrl2
);
1175 const NavigationEntry
* entry2
= contents()->GetController().GetPendingEntry();
1176 rfh2
->SendNavigate(entry2
->GetPageID(), entry2
->GetURL());
1177 EXPECT_EQ(rfh2
, main_test_rfh());
1178 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
1179 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
1180 rfh1
->OnSwappedOut();
1181 EXPECT_TRUE(rfh1
->is_swapped_out());
1182 EXPECT_EQ(RenderFrameHostImpl::STATE_SWAPPED_OUT
, rfh1
->rfh_state());
1185 // Test that we create swapped out RFHs for the opener chain when navigating an
1186 // opened tab cross-process. This allows us to support certain cross-process
1187 // JavaScript calls (http://crbug.com/99202).
1188 TEST_F(RenderFrameHostManagerTest
, CreateSwappedOutOpenerRFHs
) {
1189 const GURL
kUrl1("http://www.google.com/");
1190 const GURL
kUrl2("http://www.chromium.org/");
1191 const GURL
kChromeUrl("chrome://foo");
1193 // Navigate to an initial URL.
1194 contents()->NavigateAndCommit(kUrl1
);
1195 RenderFrameHostManager
* manager
= contents()->GetRenderManagerForTesting();
1196 TestRenderFrameHost
* rfh1
= main_test_rfh();
1197 TestRenderViewHost
* rvh1
= test_rvh();
1199 // Create 2 new tabs and simulate them being the opener chain for the main
1200 // tab. They should be in the same SiteInstance.
1201 scoped_ptr
<TestWebContents
> opener1(
1202 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1203 RenderFrameHostManager
* opener1_manager
=
1204 opener1
->GetRenderManagerForTesting();
1205 contents()->SetOpener(opener1
.get());
1207 scoped_ptr
<TestWebContents
> opener2(
1208 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1209 RenderFrameHostManager
* opener2_manager
=
1210 opener2
->GetRenderManagerForTesting();
1211 opener1
->SetOpener(opener2
.get());
1213 // Navigate to a cross-site URL (different SiteInstance but same
1214 // BrowsingInstance).
1215 contents()->NavigateAndCommit(kUrl2
);
1216 TestRenderFrameHost
* rfh2
= main_test_rfh();
1217 TestRenderViewHost
* rvh2
= test_rvh();
1218 EXPECT_NE(rfh1
->GetSiteInstance(), rfh2
->GetSiteInstance());
1219 EXPECT_TRUE(rfh1
->GetSiteInstance()->IsRelatedSiteInstance(
1220 rfh2
->GetSiteInstance()));
1222 // Ensure rvh1 is placed on swapped out list of the current tab.
1223 EXPECT_TRUE(manager
->IsOnSwappedOutList(rfh1
));
1224 EXPECT_TRUE(manager
->IsRVHOnSwappedOutList(rvh1
));
1226 manager
->GetRenderFrameProxyHost(rfh1
->GetSiteInstance())
1227 ->render_frame_host());
1229 manager
->GetSwappedOutRenderViewHost(rvh1
->GetSiteInstance()));
1231 // Ensure a swapped out RFH and RFH is created in the first opener tab.
1232 RenderFrameProxyHost
* opener1_proxy
=
1233 opener1_manager
->GetRenderFrameProxyHost(rfh2
->GetSiteInstance());
1234 RenderFrameHostImpl
* opener1_rfh
= opener1_proxy
->render_frame_host();
1235 TestRenderViewHost
* opener1_rvh
= static_cast<TestRenderViewHost
*>(
1236 opener1_manager
->GetSwappedOutRenderViewHost(rvh2
->GetSiteInstance()));
1237 EXPECT_TRUE(opener1_manager
->IsOnSwappedOutList(opener1_rfh
));
1238 EXPECT_TRUE(opener1_manager
->IsRVHOnSwappedOutList(opener1_rvh
));
1239 EXPECT_TRUE(opener1_rfh
->is_swapped_out());
1240 EXPECT_FALSE(opener1_rvh
->is_active());
1242 // Ensure a swapped out RFH and RVH is created in the second opener tab.
1243 RenderFrameProxyHost
* opener2_proxy
=
1244 opener2_manager
->GetRenderFrameProxyHost(rfh2
->GetSiteInstance());
1245 RenderFrameHostImpl
* opener2_rfh
= opener2_proxy
->render_frame_host();
1246 TestRenderViewHost
* opener2_rvh
= static_cast<TestRenderViewHost
*>(
1247 opener2_manager
->GetSwappedOutRenderViewHost(rvh2
->GetSiteInstance()));
1248 EXPECT_TRUE(opener2_manager
->IsOnSwappedOutList(opener2_rfh
));
1249 EXPECT_TRUE(opener2_manager
->IsRVHOnSwappedOutList(opener2_rvh
));
1250 EXPECT_TRUE(opener2_rfh
->is_swapped_out());
1251 EXPECT_FALSE(opener2_rvh
->is_active());
1253 // Navigate to a cross-BrowsingInstance URL.
1254 contents()->NavigateAndCommit(kChromeUrl
);
1255 TestRenderFrameHost
* rfh3
= main_test_rfh();
1256 EXPECT_NE(rfh1
->GetSiteInstance(), rfh3
->GetSiteInstance());
1257 EXPECT_FALSE(rfh1
->GetSiteInstance()->IsRelatedSiteInstance(
1258 rfh3
->GetSiteInstance()));
1260 // No scripting is allowed across BrowsingInstances, so we should not create
1261 // swapped out RVHs for the opener chain in this case.
1262 EXPECT_FALSE(opener1_manager
->GetRenderFrameProxyHost(
1263 rfh3
->GetSiteInstance()));
1264 EXPECT_FALSE(opener1_manager
->GetSwappedOutRenderViewHost(
1265 rfh3
->GetSiteInstance()));
1266 EXPECT_FALSE(opener2_manager
->GetRenderFrameProxyHost(
1267 rfh3
->GetSiteInstance()));
1268 EXPECT_FALSE(opener2_manager
->GetSwappedOutRenderViewHost(
1269 rfh3
->GetSiteInstance()));
1272 // Test that a page can disown the opener of the WebContents.
1273 TEST_F(RenderFrameHostManagerTest
, DisownOpener
) {
1274 const GURL
kUrl1("http://www.google.com/");
1275 const GURL
kUrl2("http://www.chromium.org/");
1277 // Navigate to an initial URL.
1278 contents()->NavigateAndCommit(kUrl1
);
1279 TestRenderFrameHost
* rfh1
= main_test_rfh();
1281 // Create a new tab and simulate having it be the opener for the main tab.
1282 scoped_ptr
<TestWebContents
> opener1(
1283 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1284 contents()->SetOpener(opener1
.get());
1285 EXPECT_TRUE(contents()->HasOpener());
1287 // Navigate to a cross-site URL (different SiteInstance but same
1288 // BrowsingInstance).
1289 contents()->NavigateAndCommit(kUrl2
);
1290 TestRenderFrameHost
* rfh2
= main_test_rfh();
1291 EXPECT_NE(rfh1
->GetSiteInstance(), rfh2
->GetSiteInstance());
1293 // Disown the opener from rfh2.
1294 rfh2
->DidDisownOpener();
1296 // Ensure the opener is cleared.
1297 EXPECT_FALSE(contents()->HasOpener());
1300 // Test that a page can disown a same-site opener of the WebContents.
1301 TEST_F(RenderFrameHostManagerTest
, DisownSameSiteOpener
) {
1302 const GURL
kUrl1("http://www.google.com/");
1304 // Navigate to an initial URL.
1305 contents()->NavigateAndCommit(kUrl1
);
1306 TestRenderFrameHost
* rfh1
= main_test_rfh();
1308 // Create a new tab and simulate having it be the opener for the main tab.
1309 scoped_ptr
<TestWebContents
> opener1(
1310 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1311 contents()->SetOpener(opener1
.get());
1312 EXPECT_TRUE(contents()->HasOpener());
1314 // Disown the opener from rfh1.
1315 rfh1
->DidDisownOpener();
1317 // Ensure the opener is cleared even if it is in the same process.
1318 EXPECT_FALSE(contents()->HasOpener());
1321 // Test that a page can disown the opener just as a cross-process navigation is
1323 TEST_F(RenderFrameHostManagerTest
, DisownOpenerDuringNavigation
) {
1324 const GURL
kUrl1("http://www.google.com/");
1325 const GURL
kUrl2("http://www.chromium.org/");
1327 // Navigate to an initial URL.
1328 contents()->NavigateAndCommit(kUrl1
);
1329 TestRenderFrameHost
* rfh1
= main_test_rfh();
1331 // Create a new tab and simulate having it be the opener for the main tab.
1332 scoped_ptr
<TestWebContents
> opener1(
1333 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1334 contents()->SetOpener(opener1
.get());
1335 EXPECT_TRUE(contents()->HasOpener());
1337 // Navigate to a cross-site URL (different SiteInstance but same
1338 // BrowsingInstance).
1339 contents()->NavigateAndCommit(kUrl2
);
1340 TestRenderFrameHost
* rfh2
= main_test_rfh();
1341 EXPECT_NE(rfh1
->GetSiteInstance(), rfh2
->GetSiteInstance());
1343 // Start a back navigation so that rfh1 becomes the pending RFH.
1344 contents()->GetController().GoBack();
1345 contents()->GetMainFrame()->PrepareForCommit(kUrl1
);
1347 // Disown the opener from rfh2.
1348 rfh2
->DidDisownOpener();
1350 // Ensure the opener is cleared.
1351 EXPECT_FALSE(contents()->HasOpener());
1353 // The back navigation commits.
1354 const NavigationEntry
* entry1
= contents()->GetController().GetPendingEntry();
1355 rfh1
->SendNavigate(entry1
->GetPageID(), entry1
->GetURL());
1357 // Ensure the opener is still cleared.
1358 EXPECT_FALSE(contents()->HasOpener());
1361 // Test that a page can disown the opener just after a cross-process navigation
1363 TEST_F(RenderFrameHostManagerTest
, DisownOpenerAfterNavigation
) {
1364 const GURL
kUrl1("http://www.google.com/");
1365 const GURL
kUrl2("http://www.chromium.org/");
1367 // Navigate to an initial URL.
1368 contents()->NavigateAndCommit(kUrl1
);
1369 TestRenderFrameHost
* rfh1
= main_test_rfh();
1371 // Create a new tab and simulate having it be the opener for the main tab.
1372 scoped_ptr
<TestWebContents
> opener1(
1373 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1374 contents()->SetOpener(opener1
.get());
1375 EXPECT_TRUE(contents()->HasOpener());
1377 // Navigate to a cross-site URL (different SiteInstance but same
1378 // BrowsingInstance).
1379 contents()->NavigateAndCommit(kUrl2
);
1380 TestRenderFrameHost
* rfh2
= main_test_rfh();
1381 EXPECT_NE(rfh1
->GetSiteInstance(), rfh2
->GetSiteInstance());
1383 // Commit a back navigation before the DidDisownOpener message arrives.
1384 // rfh1 will be kept alive because of the opener tab.
1385 contents()->GetController().GoBack();
1386 contents()->GetMainFrame()->PrepareForCommit(kUrl1
);
1387 const NavigationEntry
* entry1
= contents()->GetController().GetPendingEntry();
1388 rfh1
->SendNavigate(entry1
->GetPageID(), entry1
->GetURL());
1390 // Disown the opener from rfh2.
1391 rfh2
->DidDisownOpener();
1392 EXPECT_FALSE(contents()->HasOpener());
1395 // Test that we clean up swapped out RenderViewHosts when a process hosting
1396 // those associated RenderViews crashes. http://crbug.com/258993
1397 TEST_F(RenderFrameHostManagerTest
, CleanUpSwappedOutRVHOnProcessCrash
) {
1398 const GURL
kUrl1("http://www.google.com/");
1399 const GURL
kUrl2("http://www.chromium.org/");
1401 // Navigate to an initial URL.
1402 contents()->NavigateAndCommit(kUrl1
);
1403 TestRenderViewHost
* rvh1
= test_rvh();
1405 // Create a new tab as an opener for the main tab.
1406 scoped_ptr
<TestWebContents
> opener1(
1407 TestWebContents::Create(browser_context(), rvh1
->GetSiteInstance()));
1408 RenderFrameHostManager
* opener1_manager
=
1409 opener1
->GetRenderManagerForTesting();
1410 contents()->SetOpener(opener1
.get());
1412 // Make sure the new opener RVH is considered live.
1413 opener1_manager
->current_host()->CreateRenderView(
1414 base::string16(), -1, MSG_ROUTING_NONE
, -1, false);
1415 EXPECT_TRUE(opener1_manager
->current_host()->IsRenderViewLive());
1416 EXPECT_TRUE(opener1_manager
->current_frame_host()->IsRenderFrameLive());
1418 // Use a cross-process navigation in the opener to swap out the old RVH.
1419 EXPECT_FALSE(opener1_manager
->GetSwappedOutRenderViewHost(
1420 rvh1
->GetSiteInstance()));
1421 opener1
->NavigateAndCommit(kUrl2
);
1422 EXPECT_TRUE(opener1_manager
->GetSwappedOutRenderViewHost(
1423 rvh1
->GetSiteInstance()));
1425 // Fake a process crash.
1426 RenderProcessHost::RendererClosedDetails
details(
1427 base::TERMINATION_STATUS_PROCESS_CRASHED
,
1429 // TODO(nasko): Investigate whether this test can be made more realistic by
1430 // not faking the notification and just doing the RenderProcessGone. This
1431 // should also get rid of faking |set_render_view_created()| call below.
1432 NotificationService::current()->Notify(
1433 NOTIFICATION_RENDERER_PROCESS_CLOSED
,
1434 Source
<RenderProcessHost
>(rvh1
->GetProcess()),
1435 Details
<RenderProcessHost::RendererClosedDetails
>(&details
));
1436 rvh1
->set_render_view_created(false);
1438 // Ensure that the RenderFrameProxyHost stays around and the RenderFrameProxy
1440 RenderFrameProxyHost
* render_frame_proxy_host
=
1441 opener1_manager
->GetRenderFrameProxyHost(rvh1
->GetSiteInstance());
1442 EXPECT_TRUE(render_frame_proxy_host
);
1443 EXPECT_FALSE(render_frame_proxy_host
->is_render_frame_proxy_live());
1445 // Expect the swapped out RVH to exist.
1446 EXPECT_TRUE(opener1_manager
->GetSwappedOutRenderViewHost(
1447 rvh1
->GetSiteInstance()));
1449 // Reload the initial tab. This should recreate the opener's swapped out RVH
1450 // in the original SiteInstance.
1451 contents()->GetController().Reload(true);
1452 contents()->GetMainFrame()->PrepareForCommit(kUrl1
);
1453 EXPECT_EQ(opener1_manager
->GetSwappedOutRenderViewHost(
1454 rvh1
->GetSiteInstance())->GetRoutingID(),
1455 test_rvh()->opener_route_id());
1458 // Test that RenderViewHosts created for WebUI navigations are properly
1459 // granted WebUI bindings even if an unprivileged swapped out RenderViewHost
1460 // is in the same process (http://crbug.com/79918).
1461 TEST_F(RenderFrameHostManagerTest
, EnableWebUIWithSwappedOutOpener
) {
1462 set_should_create_webui(true);
1463 const GURL
kSettingsUrl("chrome://chrome/settings");
1464 const GURL
kPluginUrl("chrome://plugins");
1466 // Navigate to an initial WebUI URL.
1467 contents()->NavigateAndCommit(kSettingsUrl
);
1469 // Ensure the RVH has WebUI bindings.
1470 TestRenderViewHost
* rvh1
= test_rvh();
1471 EXPECT_TRUE(rvh1
->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1473 // Create a new tab and simulate it being the opener for the main
1474 // tab. It should be in the same SiteInstance.
1475 scoped_ptr
<TestWebContents
> opener1(
1476 TestWebContents::Create(browser_context(), rvh1
->GetSiteInstance()));
1477 RenderFrameHostManager
* opener1_manager
=
1478 opener1
->GetRenderManagerForTesting();
1479 contents()->SetOpener(opener1
.get());
1481 // Navigate to a different WebUI URL (different SiteInstance, same
1482 // BrowsingInstance).
1483 contents()->NavigateAndCommit(kPluginUrl
);
1484 TestRenderViewHost
* rvh2
= test_rvh();
1485 EXPECT_NE(rvh1
->GetSiteInstance(), rvh2
->GetSiteInstance());
1486 EXPECT_TRUE(rvh1
->GetSiteInstance()->IsRelatedSiteInstance(
1487 rvh2
->GetSiteInstance()));
1489 // Ensure a swapped out RFH and RVH is created in the first opener tab.
1490 RenderFrameProxyHost
* opener1_proxy
=
1491 opener1_manager
->GetRenderFrameProxyHost(rvh2
->GetSiteInstance());
1492 RenderFrameHostImpl
* opener1_rfh
= opener1_proxy
->render_frame_host();
1493 TestRenderViewHost
* opener1_rvh
= static_cast<TestRenderViewHost
*>(
1494 opener1_manager
->GetSwappedOutRenderViewHost(rvh2
->GetSiteInstance()));
1495 EXPECT_TRUE(opener1_manager
->IsOnSwappedOutList(opener1_rfh
));
1496 EXPECT_TRUE(opener1_manager
->IsRVHOnSwappedOutList(opener1_rvh
));
1497 EXPECT_TRUE(opener1_rfh
->is_swapped_out());
1498 EXPECT_FALSE(opener1_rvh
->is_active());
1500 // Ensure the new RVH has WebUI bindings.
1501 EXPECT_TRUE(rvh2
->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1504 // Test that we reuse the same guest SiteInstance if we navigate across sites.
1505 TEST_F(RenderFrameHostManagerTest
, NoSwapOnGuestNavigations
) {
1506 TestNotificationTracker notifications
;
1508 GURL
guest_url(std::string(kGuestScheme
).append("://abc123"));
1509 SiteInstance
* instance
=
1510 SiteInstance::CreateForURL(browser_context(), guest_url
);
1511 scoped_ptr
<TestWebContents
> web_contents(
1512 TestWebContents::Create(browser_context(), instance
));
1514 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
1516 RenderFrameHostImpl
* host
= NULL
;
1518 // 1) The first navigation. --------------------------
1519 const GURL
kUrl1("http://www.google.com/");
1520 NavigationEntryImpl
entry1(
1521 NULL
/* instance */, -1 /* page_id */, kUrl1
, Referrer(),
1522 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED
,
1523 false /* is_renderer_init */);
1524 host
= manager
->Navigate(entry1
);
1526 // The RenderFrameHost created in Init will be reused.
1527 EXPECT_TRUE(host
== manager
->current_frame_host());
1528 EXPECT_FALSE(manager
->pending_frame_host());
1529 EXPECT_EQ(manager
->current_frame_host()->GetSiteInstance(), instance
);
1532 manager
->DidNavigateFrame(host
, true);
1533 // Commit to SiteInstance should be delayed until RenderFrame commit.
1534 EXPECT_EQ(host
, manager
->current_frame_host());
1536 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
1538 // 2) Navigate to a different domain. -------------------------
1539 // Guests stay in the same process on navigation.
1540 const GURL
kUrl2("http://www.chromium.org");
1541 NavigationEntryImpl
entry2(
1542 NULL
/* instance */, -1 /* page_id */, kUrl2
,
1543 Referrer(kUrl1
, blink::WebReferrerPolicyDefault
),
1544 base::string16() /* title */, ui::PAGE_TRANSITION_LINK
,
1545 true /* is_renderer_init */);
1546 host
= manager
->Navigate(entry2
);
1548 // The RenderFrameHost created in Init will be reused.
1549 EXPECT_EQ(host
, manager
->current_frame_host());
1550 EXPECT_FALSE(manager
->pending_frame_host());
1553 manager
->DidNavigateFrame(host
, true);
1554 EXPECT_EQ(host
, manager
->current_frame_host());
1556 EXPECT_EQ(host
->GetSiteInstance(), instance
);
1559 // Test that we cancel a pending RVH if we close the tab while it's pending.
1560 // http://crbug.com/294697.
1561 TEST_F(RenderFrameHostManagerTest
, NavigateWithEarlyClose
) {
1562 TestNotificationTracker notifications
;
1564 SiteInstance
* instance
= SiteInstance::Create(browser_context());
1566 BeforeUnloadFiredWebContentsDelegate delegate
;
1567 scoped_ptr
<TestWebContents
> web_contents(
1568 TestWebContents::Create(browser_context(), instance
));
1569 web_contents
->SetDelegate(&delegate
);
1570 notifications
.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED
,
1571 Source
<WebContents
>(web_contents
.get()));
1573 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
1575 // 1) The first navigation. --------------------------
1576 const GURL
kUrl1("http://www.google.com/");
1577 NavigationEntryImpl
entry1(NULL
/* instance */, -1 /* page_id */, kUrl1
,
1578 Referrer(), base::string16() /* title */,
1579 ui::PAGE_TRANSITION_TYPED
,
1580 false /* is_renderer_init */);
1581 RenderFrameHostImpl
* host
= manager
->Navigate(entry1
);
1583 // The RenderFrameHost created in Init will be reused.
1584 EXPECT_EQ(host
, manager
->current_frame_host());
1585 EXPECT_FALSE(manager
->pending_frame_host());
1587 // We should observe a notification.
1589 notifications
.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED
));
1590 notifications
.Reset();
1593 manager
->DidNavigateFrame(host
, true);
1595 // Commit to SiteInstance should be delayed until RenderFrame commits.
1596 EXPECT_EQ(host
, manager
->current_frame_host());
1597 EXPECT_FALSE(host
->GetSiteInstance()->HasSite());
1598 host
->GetSiteInstance()->SetSite(kUrl1
);
1600 // 2) Cross-site navigate to next site. -------------------------
1601 const GURL
kUrl2("http://www.example.com");
1602 NavigationEntryImpl
entry2(
1603 NULL
/* instance */, -1 /* page_id */, kUrl2
, Referrer(),
1604 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED
,
1605 false /* is_renderer_init */);
1606 RenderFrameHostImpl
* host2
= manager
->Navigate(entry2
);
1608 // A new RenderFrameHost should be created.
1609 ASSERT_EQ(host2
, manager
->pending_frame_host());
1610 EXPECT_NE(host2
, host
);
1612 EXPECT_EQ(host
, manager
->current_frame_host());
1613 EXPECT_FALSE(manager
->current_frame_host()->is_swapped_out());
1614 EXPECT_EQ(host2
, manager
->pending_frame_host());
1616 // 3) Close the tab. -------------------------
1617 notifications
.ListenFor(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED
,
1618 Source
<RenderWidgetHost
>(host2
->render_view_host()));
1619 manager
->OnBeforeUnloadACK(false, true, base::TimeTicks());
1622 notifications
.Check1AndReset(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED
));
1623 EXPECT_FALSE(manager
->pending_frame_host());
1624 EXPECT_EQ(host
, manager
->current_frame_host());
1627 TEST_F(RenderFrameHostManagerTest
, CloseWithPendingWhileUnresponsive
) {
1628 const GURL
kUrl1("http://www.google.com/");
1629 const GURL
kUrl2("http://www.chromium.org/");
1631 CloseWebContentsDelegate close_delegate
;
1632 contents()->SetDelegate(&close_delegate
);
1634 // Navigate to the first page.
1635 contents()->NavigateAndCommit(kUrl1
);
1636 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1638 // Start to close the tab, but assume it's unresponsive.
1639 rfh1
->render_view_host()->ClosePage();
1640 EXPECT_TRUE(rfh1
->render_view_host()->is_waiting_for_close_ack());
1642 // Start a navigation to a new site.
1643 controller().LoadURL(
1644 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1645 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1646 switches::kEnableBrowserSideNavigation
)) {
1647 rfh1
->PrepareForCommit(kUrl2
);
1649 EXPECT_TRUE(contents()->cross_navigation_pending());
1651 // Simulate the unresponsiveness timer. The tab should close.
1652 contents()->RendererUnresponsive(rfh1
->render_view_host());
1653 EXPECT_TRUE(close_delegate
.is_closed());
1656 // Tests that the RenderFrameHost is properly deleted when the SwapOutACK is
1657 // received. (SwapOut and the corresponding ACK always occur after commit.)
1658 // Also tests that an early SwapOutACK is properly ignored.
1659 TEST_F(RenderFrameHostManagerTest
, DeleteFrameAfterSwapOutACK
) {
1660 const GURL
kUrl1("http://www.google.com/");
1661 const GURL
kUrl2("http://www.chromium.org/");
1663 // Navigate to the first page.
1664 contents()->NavigateAndCommit(kUrl1
);
1665 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1666 RenderFrameHostDeletedObserver
rfh_deleted_observer(rfh1
);
1667 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1669 // Navigate to new site, simulating onbeforeunload approval.
1670 controller().LoadURL(
1671 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1672 contents()->GetMainFrame()->PrepareForCommit(kUrl2
);
1673 EXPECT_TRUE(contents()->cross_navigation_pending());
1674 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1675 TestRenderFrameHost
* rfh2
= contents()->GetPendingMainFrame();
1677 // Simulate the swap out ack, unexpectedly early (before commit). It should
1679 rfh1
->OnSwappedOut();
1680 EXPECT_TRUE(contents()->cross_navigation_pending());
1681 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1683 // The new page commits.
1684 contents()->TestDidNavigate(rfh2
, 1, kUrl2
, ui::PAGE_TRANSITION_TYPED
);
1685 EXPECT_FALSE(contents()->cross_navigation_pending());
1686 EXPECT_EQ(rfh2
, contents()->GetMainFrame());
1687 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL
);
1688 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
1689 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
1691 rfh1
->frame_tree_node()->render_manager()->IsPendingDeletion(rfh1
));
1693 // Simulate the swap out ack.
1694 rfh1
->OnSwappedOut();
1696 // rfh1 should have been deleted.
1697 EXPECT_TRUE(rfh_deleted_observer
.deleted());
1701 // Tests that the RenderFrameHost is properly swapped out when the SwapOut ACK
1702 // is received. (SwapOut and the corresponding ACK always occur after commit.)
1703 TEST_F(RenderFrameHostManagerTest
, SwapOutFrameAfterSwapOutACK
) {
1704 const GURL
kUrl1("http://www.google.com/");
1705 const GURL
kUrl2("http://www.chromium.org/");
1707 // Navigate to the first page.
1708 contents()->NavigateAndCommit(kUrl1
);
1709 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1710 RenderFrameHostDeletedObserver
rfh_deleted_observer(rfh1
);
1711 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1713 // Increment the number of active frames in SiteInstanceImpl so that rfh1 is
1714 // not deleted on swap out.
1715 rfh1
->GetSiteInstance()->increment_active_frame_count();
1717 // Navigate to new site, simulating onbeforeunload approval.
1718 controller().LoadURL(
1719 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1720 contents()->GetMainFrame()->PrepareForCommit(kUrl2
);
1721 EXPECT_TRUE(contents()->cross_navigation_pending());
1722 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1723 TestRenderFrameHost
* rfh2
= contents()->GetPendingMainFrame();
1725 // The new page commits.
1726 contents()->TestDidNavigate(rfh2
, 1, kUrl2
, ui::PAGE_TRANSITION_TYPED
);
1727 EXPECT_FALSE(contents()->cross_navigation_pending());
1728 EXPECT_EQ(rfh2
, contents()->GetMainFrame());
1729 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL
);
1730 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
1731 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
1733 // Simulate the swap out ack.
1734 rfh1
->OnSwappedOut();
1736 // rfh1 should be swapped out.
1737 EXPECT_FALSE(rfh_deleted_observer
.deleted());
1738 EXPECT_TRUE(rfh1
->is_swapped_out());
1741 // Test that the RenderViewHost is properly swapped out if a navigation in the
1742 // new renderer commits before sending the SwapOut message to the old renderer.
1743 // This simulates a cross-site navigation to a synchronously committing URL
1744 // (e.g., a data URL) and ensures it works properly.
1745 TEST_F(RenderFrameHostManagerTest
,
1746 CommitNewNavigationBeforeSendingSwapOut
) {
1747 const GURL
kUrl1("http://www.google.com/");
1748 const GURL
kUrl2("http://www.chromium.org/");
1750 // Navigate to the first page.
1751 contents()->NavigateAndCommit(kUrl1
);
1752 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1753 RenderFrameHostDeletedObserver
rfh_deleted_observer(rfh1
);
1754 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1756 // Increment the number of active frames in SiteInstanceImpl so that rfh1 is
1757 // not deleted on swap out.
1758 rfh1
->GetSiteInstance()->increment_active_frame_count();
1760 // Navigate to new site, simulating onbeforeunload approval.
1761 controller().LoadURL(
1762 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1763 rfh1
->PrepareForCommit(kUrl2
);
1764 EXPECT_TRUE(contents()->cross_navigation_pending());
1765 TestRenderFrameHost
* rfh2
= contents()->GetPendingMainFrame();
1767 // The new page commits.
1768 contents()->TestDidNavigate(rfh2
, 1, kUrl2
, ui::PAGE_TRANSITION_TYPED
);
1769 EXPECT_FALSE(contents()->cross_navigation_pending());
1770 EXPECT_EQ(rfh2
, contents()->GetMainFrame());
1771 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL
);
1772 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
1773 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
1775 // Simulate the swap out ack.
1776 rfh1
->OnSwappedOut();
1778 // rfh1 should be swapped out.
1779 EXPECT_FALSE(rfh_deleted_observer
.deleted());
1780 EXPECT_TRUE(rfh1
->is_swapped_out());
1783 // Test that a RenderFrameHost is properly deleted or swapped out when a
1784 // cross-site navigation is cancelled.
1785 TEST_F(RenderFrameHostManagerTest
,
1786 CancelPendingProperlyDeletesOrSwaps
) {
1787 const GURL
kUrl1("http://www.google.com/");
1788 const GURL
kUrl2("http://www.chromium.org/");
1789 RenderFrameHostImpl
* pending_rfh
= NULL
;
1790 base::TimeTicks now
= base::TimeTicks::Now();
1792 // Navigate to the first page.
1793 contents()->NavigateAndCommit(kUrl1
);
1794 TestRenderFrameHost
* rfh1
= main_test_rfh();
1795 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1797 // Navigate to a new site, starting a cross-site navigation.
1798 controller().LoadURL(
1799 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1801 pending_rfh
= contents()->GetFrameTree()->root()->render_manager()
1802 ->pending_frame_host();
1803 RenderFrameHostDeletedObserver
rfh_deleted_observer(pending_rfh
);
1805 // Cancel the navigation by simulating a declined beforeunload dialog.
1806 contents()->GetMainFrame()->OnMessageReceived(
1807 FrameHostMsg_BeforeUnload_ACK(0, false, now
, now
));
1808 EXPECT_FALSE(contents()->cross_navigation_pending());
1810 // Since the pending RFH is the only one for the new SiteInstance, it should
1812 EXPECT_TRUE(rfh_deleted_observer
.deleted());
1815 // Start another cross-site navigation.
1816 controller().LoadURL(
1817 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1819 pending_rfh
= contents()->GetFrameTree()->root()->render_manager()
1820 ->pending_frame_host();
1821 RenderFrameHostDeletedObserver
rfh_deleted_observer(pending_rfh
);
1823 // Increment the number of active frames in the new SiteInstance, which will
1824 // cause the pending RFH to be swapped out instead of deleted.
1825 pending_rfh
->GetSiteInstance()->increment_active_frame_count();
1827 contents()->GetMainFrame()->OnMessageReceived(
1828 FrameHostMsg_BeforeUnload_ACK(0, false, now
, now
));
1829 EXPECT_FALSE(contents()->cross_navigation_pending());
1830 EXPECT_FALSE(rfh_deleted_observer
.deleted());
1834 // Test that a pending RenderFrameHost in a non-root frame tree node is properly
1835 // deleted when the node is detached. Motivated by http://crbug.com/441357
1836 TEST_F(RenderFrameHostManagerTest
, DetachPendingChild
) {
1837 base::CommandLine::ForCurrentProcess()->AppendSwitch(
1838 switches::kSitePerProcess
);
1840 const GURL
kUrl1("http://www.google.com/");
1841 const GURL
kUrl2("http://webkit.org/");
1843 RenderFrameHostImpl
* host
= NULL
;
1845 contents()->NavigateAndCommit(kUrl1
);
1846 contents()->GetMainFrame()->OnCreateChildFrame(
1847 contents()->GetMainFrame()->GetProcess()->GetNextRoutingID(),
1848 std::string("frame_name"), SandboxFlags::NONE
);
1849 RenderFrameHostManager
* manager
=
1850 contents()->GetFrameTree()->root()->child_at(0)->render_manager();
1852 // 1) The first navigation. --------------------------
1853 NavigationEntryImpl
entry1(NULL
/* instance */, -1 /* page_id */, kUrl1
,
1854 Referrer(), base::string16() /* title */,
1855 ui::PAGE_TRANSITION_TYPED
,
1856 false /* is_renderer_init */);
1857 host
= GetFrameHostForNavigation(manager
, entry1
);
1859 // The RenderFrameHost created in Init will be reused.
1860 EXPECT_TRUE(host
== manager
->current_frame_host());
1861 EXPECT_FALSE(GetPendingFrameHost(manager
));
1864 manager
->DidNavigateFrame(host
, true);
1865 // Commit to SiteInstance should be delayed until RenderFrame commit.
1866 EXPECT_TRUE(host
== manager
->current_frame_host());
1868 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
1870 // 2) Cross-site navigate to next site. --------------
1871 NavigationEntryImpl
entry2(NULL
/* instance */, -1 /* page_id */, kUrl2
,
1872 Referrer(kUrl1
, blink::WebReferrerPolicyDefault
),
1873 base::string16() /* title */,
1874 ui::PAGE_TRANSITION_LINK
,
1875 false /* is_renderer_init */);
1876 host
= GetFrameHostForNavigation(manager
, entry2
);
1878 // A new RenderFrameHost should be created.
1879 EXPECT_TRUE(GetPendingFrameHost(manager
));
1880 ASSERT_EQ(host
, GetPendingFrameHost(manager
));
1881 ASSERT_NE(manager
->current_frame_host(), GetPendingFrameHost(manager
));
1882 EXPECT_FALSE(contents()->cross_navigation_pending())
1883 << "There should be no top-level pending navigation.";
1885 RenderFrameHostDeletedObserver
delete_watcher(GetPendingFrameHost(manager
));
1886 EXPECT_FALSE(delete_watcher
.deleted());
1888 // Extend the lifetime of the child frame's SiteInstance, pretending
1889 // that there is another reference to it.
1890 scoped_refptr
<SiteInstanceImpl
> site_instance
=
1891 GetPendingFrameHost(manager
)->GetSiteInstance();
1892 EXPECT_TRUE(site_instance
->HasSite());
1893 EXPECT_NE(site_instance
, contents()->GetSiteInstance());
1894 EXPECT_EQ(1U, site_instance
->active_frame_count());
1895 site_instance
->increment_active_frame_count();
1896 EXPECT_EQ(2U, site_instance
->active_frame_count());
1898 // Now detach the child FrameTreeNode. This should kill the pending host.
1899 manager
->current_frame_host()->OnMessageReceived(
1900 FrameHostMsg_Detach(manager
->current_frame_host()->GetRoutingID()));
1902 EXPECT_TRUE(delete_watcher
.deleted());
1904 EXPECT_EQ(1U, site_instance
->active_frame_count());
1905 site_instance
->decrement_active_frame_count();
1908 // TODO(nick): Currently a proxy to the removed frame lingers in the parent.
1909 // Enable this assert below once the proxies to the subframe are correctly
1910 // cleaned up after detach. http://crbug.com/444955.
1911 ASSERT_TRUE(site_instance
->HasOneRef())
1912 << "This SiteInstance should be destroyable now.";
1916 } // namespace content