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/compositor/test/no_transport_image_transport_factory.h"
11 #include "content/browser/frame_host/cross_site_transferring_request.h"
12 #include "content/browser/frame_host/navigation_controller_impl.h"
13 #include "content/browser/frame_host/navigation_entry_impl.h"
14 #include "content/browser/frame_host/navigation_request.h"
15 #include "content/browser/frame_host/navigator.h"
16 #include "content/browser/frame_host/render_frame_host_manager.h"
17 #include "content/browser/frame_host/render_frame_proxy_host.h"
18 #include "content/browser/site_instance_impl.h"
19 #include "content/browser/webui/web_ui_controller_factory_registry.h"
20 #include "content/common/frame_messages.h"
21 #include "content/common/view_messages.h"
22 #include "content/public/browser/notification_details.h"
23 #include "content/public/browser/notification_service.h"
24 #include "content/public/browser/notification_source.h"
25 #include "content/public/browser/notification_types.h"
26 #include "content/public/browser/render_process_host.h"
27 #include "content/public/browser/render_widget_host_iterator.h"
28 #include "content/public/browser/web_contents_delegate.h"
29 #include "content/public/browser/web_contents_observer.h"
30 #include "content/public/browser/web_ui_controller.h"
31 #include "content/public/common/bindings_policy.h"
32 #include "content/public/common/content_switches.h"
33 #include "content/public/common/javascript_message_type.h"
34 #include "content/public/common/url_constants.h"
35 #include "content/public/common/url_utils.h"
36 #include "content/public/test/mock_render_process_host.h"
37 #include "content/public/test/test_notification_tracker.h"
38 #include "content/test/test_content_browser_client.h"
39 #include "content/test/test_content_client.h"
40 #include "content/test/test_render_frame_host.h"
41 #include "content/test/test_render_view_host.h"
42 #include "content/test/test_web_contents.h"
43 #include "net/base/load_flags.h"
44 #include "testing/gtest/include/gtest/gtest.h"
45 #include "third_party/WebKit/public/web/WebSandboxFlags.h"
46 #include "ui/base/page_transition_types.h"
51 class RenderFrameHostManagerTestWebUIControllerFactory
52 : public WebUIControllerFactory
{
54 RenderFrameHostManagerTestWebUIControllerFactory()
55 : should_create_webui_(false) {
57 ~RenderFrameHostManagerTestWebUIControllerFactory() override
{}
59 void set_should_create_webui(bool should_create_webui
) {
60 should_create_webui_
= should_create_webui
;
63 // WebUIFactory implementation.
64 WebUIController
* CreateWebUIControllerForURL(WebUI
* web_ui
,
65 const GURL
& url
) const override
{
66 if (!(should_create_webui_
&& HasWebUIScheme(url
)))
68 return new WebUIController(web_ui
);
71 WebUI::TypeID
GetWebUIType(BrowserContext
* browser_context
,
72 const GURL
& url
) const override
{
73 return WebUI::kNoWebUI
;
76 bool UseWebUIForURL(BrowserContext
* browser_context
,
77 const GURL
& url
) const override
{
78 return HasWebUIScheme(url
);
81 bool UseWebUIBindingsForURL(BrowserContext
* browser_context
,
82 const GURL
& url
) const override
{
83 return HasWebUIScheme(url
);
87 bool should_create_webui_
;
89 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostManagerTestWebUIControllerFactory
);
92 class BeforeUnloadFiredWebContentsDelegate
: public WebContentsDelegate
{
94 BeforeUnloadFiredWebContentsDelegate() {}
95 ~BeforeUnloadFiredWebContentsDelegate() override
{}
97 void BeforeUnloadFired(WebContents
* web_contents
,
99 bool* proceed_to_fire_unload
) override
{
100 *proceed_to_fire_unload
= proceed
;
104 DISALLOW_COPY_AND_ASSIGN(BeforeUnloadFiredWebContentsDelegate
);
107 class CloseWebContentsDelegate
: public WebContentsDelegate
{
109 CloseWebContentsDelegate() : close_called_(false) {}
110 ~CloseWebContentsDelegate() override
{}
112 void CloseContents(WebContents
* web_contents
) override
{
113 close_called_
= true;
116 bool is_closed() { return close_called_
; }
119 DISALLOW_COPY_AND_ASSIGN(CloseWebContentsDelegate
);
124 // This observer keeps track of the last deleted RenderViewHost to avoid
125 // accessing it and causing use-after-free condition.
126 class RenderViewHostDeletedObserver
: public WebContentsObserver
{
128 RenderViewHostDeletedObserver(RenderViewHost
* rvh
)
129 : WebContentsObserver(WebContents::FromRenderViewHost(rvh
)),
130 process_id_(rvh
->GetProcess()->GetID()),
131 routing_id_(rvh
->GetRoutingID()),
135 void RenderViewDeleted(RenderViewHost
* render_view_host
) override
{
136 if (render_view_host
->GetProcess()->GetID() == process_id_
&&
137 render_view_host
->GetRoutingID() == routing_id_
) {
151 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDeletedObserver
);
154 // This observer keeps track of the last created RenderFrameHost to allow tests
155 // to ensure that no RenderFrameHost objects are created when not expected.
156 class RenderFrameHostCreatedObserver
: public WebContentsObserver
{
158 RenderFrameHostCreatedObserver(WebContents
* web_contents
)
159 : WebContentsObserver(web_contents
),
163 void RenderFrameCreated(RenderFrameHost
* render_frame_host
) override
{
174 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostCreatedObserver
);
177 // This observer keeps track of the last deleted RenderFrameHost to avoid
178 // accessing it and causing use-after-free condition.
179 class RenderFrameHostDeletedObserver
: public WebContentsObserver
{
181 RenderFrameHostDeletedObserver(RenderFrameHost
* rfh
)
182 : WebContentsObserver(WebContents::FromRenderFrameHost(rfh
)),
183 process_id_(rfh
->GetProcess()->GetID()),
184 routing_id_(rfh
->GetRoutingID()),
188 void RenderFrameDeleted(RenderFrameHost
* render_frame_host
) override
{
189 if (render_frame_host
->GetProcess()->GetID() == process_id_
&&
190 render_frame_host
->GetRoutingID() == routing_id_
) {
204 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostDeletedObserver
);
207 // This WebContents observer keep track of its RVH change.
208 class RenderViewHostChangedObserver
: public WebContentsObserver
{
210 RenderViewHostChangedObserver(WebContents
* web_contents
)
211 : WebContentsObserver(web_contents
), host_changed_(false) {}
213 // WebContentsObserver.
214 void RenderViewHostChanged(RenderViewHost
* old_host
,
215 RenderViewHost
* new_host
) override
{
216 host_changed_
= true;
219 bool DidHostChange() {
220 bool host_changed
= host_changed_
;
225 void Reset() { host_changed_
= false; }
229 DISALLOW_COPY_AND_ASSIGN(RenderViewHostChangedObserver
);
232 // This observer is used to check whether IPC messages are being filtered for
233 // swapped out RenderFrameHost objects. It observes the plugin crash and favicon
234 // update events, which the FilterMessagesWhileSwappedOut test simulates being
235 // sent. The test is successful if the event is not observed.
236 // See http://crbug.com/351815
237 class PluginFaviconMessageObserver
: public WebContentsObserver
{
239 PluginFaviconMessageObserver(WebContents
* web_contents
)
240 : WebContentsObserver(web_contents
),
241 plugin_crashed_(false),
242 favicon_received_(false) { }
244 void PluginCrashed(const base::FilePath
& plugin_path
,
245 base::ProcessId plugin_pid
) override
{
246 plugin_crashed_
= true;
249 void DidUpdateFaviconURL(const std::vector
<FaviconURL
>& candidates
) override
{
250 favicon_received_
= true;
253 bool plugin_crashed() {
254 return plugin_crashed_
;
257 bool favicon_received() {
258 return favicon_received_
;
262 bool plugin_crashed_
;
263 bool favicon_received_
;
265 DISALLOW_COPY_AND_ASSIGN(PluginFaviconMessageObserver
);
270 class RenderFrameHostManagerTest
: public RenderViewHostImplTestHarness
{
272 void SetUp() override
{
273 RenderViewHostImplTestHarness::SetUp();
274 WebUIControllerFactory::RegisterFactory(&factory_
);
275 #if !defined(OS_ANDROID)
276 ImageTransportFactory::InitializeForUnitTests(
277 make_scoped_ptr(new NoTransportImageTransportFactory
));
281 void TearDown() override
{
282 #if !defined(OS_ANDROID)
283 ImageTransportFactory::Terminate();
285 RenderViewHostImplTestHarness::TearDown();
286 WebUIControllerFactory::UnregisterFactoryForTesting(&factory_
);
289 void set_should_create_webui(bool should_create_webui
) {
290 factory_
.set_should_create_webui(should_create_webui
);
293 void NavigateActiveAndCommit(const GURL
& url
) {
294 // Note: we navigate the active RenderFrameHost because previous navigations
295 // won't have committed yet, so NavigateAndCommit does the wrong thing
297 controller().LoadURL(
298 url
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
299 int entry_id
= controller().GetPendingEntry()->GetUniqueID();
301 // Simulate the BeforeUnload_ACK that is received from the current renderer
302 // for a cross-site navigation.
303 // PlzNavigate: it is necessary to call PrepareForCommit before getting the
304 // main and the pending frame because when we are trying to navigate to a
305 // WebUI from a new tab, a RenderFrameHost is created to display it that is
306 // committed immediately (since it is a new tab). Therefore the main frame
307 // is replaced without a pending frame being created, and we don't get the
308 // right values for the RFH to navigate: we try to use the old one that has
309 // been deleted in the meantime.
310 contents()->GetMainFrame()->PrepareForCommit();
312 TestRenderFrameHost
* old_rfh
= contents()->GetMainFrame();
313 TestRenderFrameHost
* active_rfh
= contents()->GetPendingMainFrame()
314 ? contents()->GetPendingMainFrame()
316 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, old_rfh
->rfh_state());
318 // Commit the navigation with a new page ID.
319 int32 max_page_id
= contents()->GetMaxPageIDForSiteInstance(
320 active_rfh
->GetSiteInstance());
322 // Use an observer to avoid accessing a deleted renderer later on when the
323 // state is being checked.
324 RenderFrameHostDeletedObserver
rfh_observer(old_rfh
);
325 RenderViewHostDeletedObserver
rvh_observer(old_rfh
->GetRenderViewHost());
326 active_rfh
->SendNavigate(max_page_id
+ 1, entry_id
, true, url
);
328 // Make sure that we start to run the unload handler at the time of commit.
329 bool expecting_rfh_shutdown
= false;
330 if (old_rfh
!= active_rfh
&& !rfh_observer
.deleted()) {
331 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
,
332 old_rfh
->rfh_state());
333 if (!old_rfh
->GetSiteInstance()->active_frame_count() ||
334 base::CommandLine::ForCurrentProcess()->HasSwitch(
335 switches::kSitePerProcess
)) {
336 expecting_rfh_shutdown
= true;
338 old_rfh
->frame_tree_node()->render_manager()->IsPendingDeletion(
343 // Simulate the swap out ACK coming from the pending renderer. This should
344 // either shut down the old RFH or leave it in a swapped out state.
345 if (old_rfh
!= active_rfh
) {
346 old_rfh
->OnSwappedOut();
347 if (expecting_rfh_shutdown
) {
348 EXPECT_TRUE(rfh_observer
.deleted());
349 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
350 switches::kSitePerProcess
)) {
351 EXPECT_TRUE(rvh_observer
.deleted());
354 EXPECT_EQ(RenderFrameHostImpl::STATE_SWAPPED_OUT
,
355 old_rfh
->rfh_state());
358 EXPECT_EQ(active_rfh
, contents()->GetMainFrame());
359 EXPECT_EQ(NULL
, contents()->GetPendingMainFrame());
362 bool ShouldSwapProcesses(RenderFrameHostManager
* manager
,
363 const NavigationEntryImpl
* current_entry
,
364 const NavigationEntryImpl
* new_entry
) const {
366 BrowserContext
* browser_context
=
367 manager
->delegate_
->GetControllerForRenderManager().GetBrowserContext();
368 const GURL
& current_effective_url
= current_entry
?
369 SiteInstanceImpl::GetEffectiveURL(browser_context
,
370 current_entry
->GetURL()) :
371 manager
->render_frame_host_
->GetSiteInstance()->GetSiteURL();
372 bool current_is_view_source_mode
= current_entry
?
373 current_entry
->IsViewSourceMode() : new_entry
->IsViewSourceMode();
374 return manager
->ShouldSwapBrowsingInstancesForNavigation(
375 current_effective_url
,
376 current_is_view_source_mode
,
377 new_entry
->site_instance(),
378 SiteInstanceImpl::GetEffectiveURL(browser_context
, new_entry
->GetURL()),
379 new_entry
->IsViewSourceMode());
382 // Creates a test RenderFrameHost that's swapped out.
383 TestRenderFrameHost
* CreateSwappedOutRenderFrameHost() {
384 const GURL
kChromeURL("chrome://foo");
385 const GURL
kDestUrl("http://www.google.com/");
387 // Navigate our first tab to a chrome url and then to the destination.
388 NavigateActiveAndCommit(kChromeURL
);
389 TestRenderFrameHost
* ntp_rfh
= contents()->GetMainFrame();
391 // Navigate to a cross-site URL.
392 contents()->GetController().LoadURL(
393 kDestUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
394 int entry_id
= contents()->GetController().GetPendingEntry()->GetUniqueID();
395 contents()->GetMainFrame()->PrepareForCommit();
396 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
398 // Manually increase the number of active frames in the
399 // SiteInstance that ntp_rfh belongs to, to prevent it from being
400 // destroyed when it gets swapped out.
401 ntp_rfh
->GetSiteInstance()->increment_active_frame_count();
403 TestRenderFrameHost
* dest_rfh
= contents()->GetPendingMainFrame();
405 EXPECT_NE(ntp_rfh
, dest_rfh
);
407 // BeforeUnload finishes.
408 ntp_rfh
->SendBeforeUnloadACK(true);
410 dest_rfh
->SendNavigate(101, entry_id
, true, kDestUrl
);
411 ntp_rfh
->OnSwappedOut();
413 EXPECT_TRUE(ntp_rfh
->is_swapped_out());
417 // Returns the RenderFrameHost that should be used in the navigation to
419 RenderFrameHostImpl
* NavigateToEntry(
420 RenderFrameHostManager
* manager
,
421 const NavigationEntryImpl
& entry
) {
422 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
423 switches::kEnableBrowserSideNavigation
)) {
424 scoped_ptr
<NavigationRequest
> navigation_request
=
425 NavigationRequest::CreateBrowserInitiated(
426 manager
->frame_tree_node_
, entry
, FrameMsg_Navigate_Type::NORMAL
,
427 base::TimeTicks::Now(),
428 static_cast<NavigationControllerImpl
*>(&controller()));
429 TestRenderFrameHost
* frame_host
= static_cast<TestRenderFrameHost
*>(
430 manager
->GetFrameHostForNavigation(*navigation_request
));
432 frame_host
->set_pending_commit(true);
435 return manager
->Navigate(entry
);
438 // Returns the pending RenderFrameHost.
439 // PlzNavigate: returns the speculative RenderFrameHost.
440 RenderFrameHostImpl
* GetPendingFrameHost(
441 RenderFrameHostManager
* manager
) {
442 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
443 switches::kEnableBrowserSideNavigation
)) {
444 return manager
->speculative_render_frame_host_
.get();
446 return manager
->pending_frame_host();
450 RenderFrameHostManagerTestWebUIControllerFactory factory_
;
453 // Tests that when you navigate from a chrome:// url to another page, and
454 // then do that same thing in another tab, that the two resulting pages have
455 // different SiteInstances, BrowsingInstances, and RenderProcessHosts. This is
456 // a regression test for bug 9364.
457 TEST_F(RenderFrameHostManagerTest
, NewTabPageProcesses
) {
458 set_should_create_webui(true);
459 const GURL
kChromeUrl("chrome://foo");
460 const GURL
kDestUrl("http://www.google.com/");
462 // Navigate our first tab to the chrome url and then to the destination,
463 // ensuring we grant bindings to the chrome URL.
464 NavigateActiveAndCommit(kChromeUrl
);
465 EXPECT_TRUE(active_rvh()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
466 NavigateActiveAndCommit(kDestUrl
);
468 EXPECT_FALSE(contents()->GetPendingMainFrame());
470 // Make a second tab.
471 scoped_ptr
<TestWebContents
> contents2(
472 TestWebContents::Create(browser_context(), NULL
));
474 // Load the two URLs in the second tab. Note that the first navigation creates
475 // a RFH that's not pending (since there is no cross-site transition), so
476 // we use the committed one.
477 contents2
->GetController().LoadURL(
478 kChromeUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
479 int entry_id
= contents2
->GetController().GetPendingEntry()->GetUniqueID();
480 contents2
->GetMainFrame()->PrepareForCommit();
481 TestRenderFrameHost
* ntp_rfh2
= contents2
->GetMainFrame();
482 EXPECT_FALSE(contents2
->CrossProcessNavigationPending());
483 ntp_rfh2
->SendNavigate(100, entry_id
, true, kChromeUrl
);
485 // The second one is the opposite, creating a cross-site transition and
486 // requiring a beforeunload ack.
487 contents2
->GetController().LoadURL(
488 kDestUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
489 entry_id
= contents2
->GetController().GetPendingEntry()->GetUniqueID();
490 contents2
->GetMainFrame()->PrepareForCommit();
491 EXPECT_TRUE(contents2
->CrossProcessNavigationPending());
492 TestRenderFrameHost
* dest_rfh2
= contents2
->GetPendingMainFrame();
493 ASSERT_TRUE(dest_rfh2
);
495 dest_rfh2
->SendNavigate(101, entry_id
, true, kDestUrl
);
497 // The two RFH's should be different in every way.
498 EXPECT_NE(contents()->GetMainFrame()->GetProcess(), dest_rfh2
->GetProcess());
499 EXPECT_NE(contents()->GetMainFrame()->GetSiteInstance(),
500 dest_rfh2
->GetSiteInstance());
501 EXPECT_FALSE(dest_rfh2
->GetSiteInstance()->IsRelatedSiteInstance(
502 contents()->GetMainFrame()->GetSiteInstance()));
504 // Navigate both to the new tab page, and verify that they share a
505 // RenderProcessHost (not a SiteInstance).
506 NavigateActiveAndCommit(kChromeUrl
);
507 EXPECT_FALSE(contents()->GetPendingMainFrame());
509 contents2
->GetController().LoadURL(
510 kChromeUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
511 entry_id
= contents2
->GetController().GetPendingEntry()->GetUniqueID();
512 contents2
->GetMainFrame()->PrepareForCommit();
513 contents2
->GetPendingMainFrame()->SendNavigate(102, entry_id
, true,
516 EXPECT_NE(contents()->GetMainFrame()->GetSiteInstance(),
517 contents2
->GetMainFrame()->GetSiteInstance());
518 EXPECT_EQ(contents()->GetMainFrame()->GetSiteInstance()->GetProcess(),
519 contents2
->GetMainFrame()->GetSiteInstance()->GetProcess());
522 // Ensure that the browser ignores most IPC messages that arrive from a
523 // RenderViewHost that has been swapped out. We do not want to take
524 // action on requests from a non-active renderer. The main exception is
525 // for synchronous messages, which cannot be ignored without leaving the
526 // renderer in a stuck state. See http://crbug.com/93427.
527 TEST_F(RenderFrameHostManagerTest
, FilterMessagesWhileSwappedOut
) {
528 const GURL
kChromeURL("chrome://foo");
529 const GURL
kDestUrl("http://www.google.com/");
530 std::vector
<FaviconURL
> icons
;
532 // Navigate our first tab to a chrome url and then to the destination.
533 NavigateActiveAndCommit(kChromeURL
);
534 TestRenderFrameHost
* ntp_rfh
= contents()->GetMainFrame();
535 TestRenderViewHost
* ntp_rvh
= ntp_rfh
->GetRenderViewHost();
537 // Send an update favicon message and make sure it works.
539 PluginFaviconMessageObserver
observer(contents());
540 EXPECT_TRUE(ntp_rfh
->GetRenderViewHost()->OnMessageReceived(
541 ViewHostMsg_UpdateFaviconURL(
542 ntp_rfh
->GetRenderViewHost()->GetRoutingID(), icons
)));
543 EXPECT_TRUE(observer
.favicon_received());
545 // Create one more frame in the same SiteInstance where ntp_rfh
546 // exists so that it doesn't get deleted on navigation to another
548 ntp_rfh
->GetSiteInstance()->increment_active_frame_count();
550 // Navigate to a cross-site URL.
551 NavigateActiveAndCommit(kDestUrl
);
552 TestRenderFrameHost
* dest_rfh
= contents()->GetMainFrame();
553 ASSERT_TRUE(dest_rfh
);
554 EXPECT_NE(ntp_rfh
, dest_rfh
);
556 // The new RVH should be able to update its favicon.
558 PluginFaviconMessageObserver
observer(contents());
560 dest_rfh
->GetRenderViewHost()->OnMessageReceived(
561 ViewHostMsg_UpdateFaviconURL(
562 dest_rfh
->GetRenderViewHost()->GetRoutingID(), icons
)));
563 EXPECT_TRUE(observer
.favicon_received());
566 // The old renderer, being slow, now updates the favicon. It should be
567 // filtered out and not take effect.
569 PluginFaviconMessageObserver
observer(contents());
571 ntp_rvh
->OnMessageReceived(
572 ViewHostMsg_UpdateFaviconURL(
573 dest_rfh
->GetRenderViewHost()->GetRoutingID(), icons
)));
574 EXPECT_FALSE(observer
.favicon_received());
577 // In --site-per-process, the RenderFrameHost is deleted on cross-process
578 // navigation, so the rest of the test case doesn't apply.
579 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
580 switches::kSitePerProcess
)) {
584 #if defined(ENABLE_PLUGINS)
585 // The same logic should apply to RenderFrameHosts as well and routing through
586 // swapped out RFH shouldn't be allowed. Use a PluginCrashObserver to check
587 // if the IPC message is allowed through or not.
589 PluginFaviconMessageObserver
observer(contents());
590 EXPECT_TRUE(ntp_rfh
->OnMessageReceived(
591 FrameHostMsg_PluginCrashed(
592 ntp_rfh
->GetRoutingID(), base::FilePath(), 0)));
593 EXPECT_FALSE(observer
.plugin_crashed());
597 // We cannot filter out synchronous IPC messages, because the renderer would
598 // be left waiting for a reply. We pick RunBeforeUnloadConfirm as an example
599 // that can run easily within a unit test, and that needs to receive a reply
600 // without showing an actual dialog.
601 MockRenderProcessHost
* ntp_process_host
= ntp_rfh
->GetProcess();
602 ntp_process_host
->sink().ClearMessages();
603 const base::string16 msg
= base::ASCIIToUTF16("Message");
605 base::string16 unused
;
606 FrameHostMsg_RunBeforeUnloadConfirm
before_unload_msg(
607 ntp_rfh
->GetRoutingID(), kChromeURL
, msg
, false, &result
, &unused
);
608 // Enable pumping for check in BrowserMessageFilter::CheckCanDispatchOnUI.
609 before_unload_msg
.EnableMessagePumping();
610 EXPECT_TRUE(ntp_rfh
->OnMessageReceived(before_unload_msg
));
611 EXPECT_TRUE(ntp_process_host
->sink().GetUniqueMessageMatching(IPC_REPLY_ID
));
613 // Also test RunJavaScriptMessage.
614 ntp_process_host
->sink().ClearMessages();
615 FrameHostMsg_RunJavaScriptMessage
js_msg(
616 ntp_rfh
->GetRoutingID(), msg
, msg
, kChromeURL
,
617 JAVASCRIPT_MESSAGE_TYPE_CONFIRM
, &result
, &unused
);
618 js_msg
.EnableMessagePumping();
619 EXPECT_TRUE(ntp_rfh
->OnMessageReceived(js_msg
));
620 EXPECT_TRUE(ntp_process_host
->sink().GetUniqueMessageMatching(IPC_REPLY_ID
));
623 // Test that the ViewHostMsg_UpdateFaviconURL IPC message is ignored if the
624 // renderer is in the STATE_PENDING_SWAP_OUT_STATE. The favicon code assumes
625 // that it only gets ViewHostMsg_UpdateFaviconURL messages for the most recently
626 // committed navigation for each WebContentsImpl.
627 TEST_F(RenderFrameHostManagerTest
, UpdateFaviconURLWhilePendingSwapOut
) {
628 const GURL
kChromeURL("chrome://foo");
629 const GURL
kDestUrl("http://www.google.com/");
630 std::vector
<FaviconURL
> icons
;
632 // Navigate our first tab to a chrome url and then to the destination.
633 NavigateActiveAndCommit(kChromeURL
);
634 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
636 // Send an update favicon message and make sure it works.
638 PluginFaviconMessageObserver
observer(contents());
639 EXPECT_TRUE(rfh1
->GetRenderViewHost()->OnMessageReceived(
640 ViewHostMsg_UpdateFaviconURL(
641 rfh1
->GetRenderViewHost()->GetRoutingID(), icons
)));
642 EXPECT_TRUE(observer
.favicon_received());
645 // Create one more frame in the same SiteInstance where |rfh1| exists so that
646 // it doesn't get deleted on navigation to another site.
647 rfh1
->GetSiteInstance()->increment_active_frame_count();
649 // Navigate to a cross-site URL and commit the new page.
650 controller().LoadURL(
651 kDestUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
652 int entry_id
= controller().GetPendingEntry()->GetUniqueID();
653 contents()->GetMainFrame()->PrepareForCommit();
654 TestRenderFrameHost
* rfh2
= contents()->GetPendingMainFrame();
655 contents()->TestDidNavigate(rfh2
, 1, entry_id
, true, kDestUrl
,
656 ui::PAGE_TRANSITION_TYPED
);
657 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
658 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
660 // The new RVH should be able to update its favicons.
662 PluginFaviconMessageObserver
observer(contents());
663 EXPECT_TRUE(rfh2
->GetRenderViewHost()->OnMessageReceived(
664 ViewHostMsg_UpdateFaviconURL(rfh2
->GetRenderViewHost()->GetRoutingID(),
666 EXPECT_TRUE(observer
.favicon_received());
669 // The old renderer, being slow, now updates its favicons. The message should
672 PluginFaviconMessageObserver
observer(contents());
673 EXPECT_TRUE(rfh1
->GetRenderViewHost()->OnMessageReceived(
674 ViewHostMsg_UpdateFaviconURL(rfh1
->GetRenderViewHost()->GetRoutingID(),
676 EXPECT_FALSE(observer
.favicon_received());
680 // Ensure that frames aren't added to the frame tree, if the message is coming
681 // from a process different than the parent frame's current RenderFrameHost
682 // process. Otherwise it is possible to have collisions of routing ids, as they
683 // are scoped per process. See https://crbug.com/415059.
684 TEST_F(RenderFrameHostManagerTest
, DropCreateChildFrameWhileSwappedOut
) {
685 const GURL
kUrl1("http://foo.com");
686 const GURL
kUrl2("http://www.google.com/");
688 // This test is invalid in --site-per-process mode, as swapped-out is no
690 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
691 switches::kSitePerProcess
)) {
695 // Navigate to the first site.
696 NavigateActiveAndCommit(kUrl1
);
697 TestRenderFrameHost
* initial_rfh
= contents()->GetMainFrame();
699 RenderFrameHostCreatedObserver
observer(contents());
700 initial_rfh
->OnCreateChildFrame(
701 initial_rfh
->GetProcess()->GetNextRoutingID(),
702 blink::WebTreeScopeType::Document
, std::string(),
703 blink::WebSandboxFlags::None
);
704 EXPECT_TRUE(observer
.created());
707 // Create one more frame in the same SiteInstance where initial_rfh
708 // exists so that initial_rfh doesn't get deleted on navigation to another
710 initial_rfh
->GetSiteInstance()->increment_active_frame_count();
712 // Navigate to a cross-site URL.
713 NavigateActiveAndCommit(kUrl2
);
714 EXPECT_TRUE(initial_rfh
->is_swapped_out());
716 TestRenderFrameHost
* dest_rfh
= contents()->GetMainFrame();
717 ASSERT_TRUE(dest_rfh
);
718 EXPECT_NE(initial_rfh
, dest_rfh
);
721 // Since the old RFH is now swapped out, it shouldn't process any messages
722 // to create child frames.
723 RenderFrameHostCreatedObserver
observer(contents());
724 initial_rfh
->OnCreateChildFrame(
725 initial_rfh
->GetProcess()->GetNextRoutingID(),
726 blink::WebTreeScopeType::Document
, std::string(),
727 blink::WebSandboxFlags::None
);
728 EXPECT_FALSE(observer
.created());
732 TEST_F(RenderFrameHostManagerTest
, WhiteListSwapCompositorFrame
) {
733 // TODO(nasko): Check with kenrb whether this test can be rewritten and
734 // whether it makes sense when swapped out is replaced with proxies.
735 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
736 switches::kSitePerProcess
)) {
739 TestRenderFrameHost
* swapped_out_rfh
= CreateSwappedOutRenderFrameHost();
740 TestRenderWidgetHostView
* swapped_out_rwhv
=
741 static_cast<TestRenderWidgetHostView
*>(
742 swapped_out_rfh
->GetRenderViewHost()->GetView());
743 EXPECT_FALSE(swapped_out_rwhv
->did_swap_compositor_frame());
745 MockRenderProcessHost
* process_host
= swapped_out_rfh
->GetProcess();
746 process_host
->sink().ClearMessages();
748 cc::CompositorFrame frame
;
749 ViewHostMsg_SwapCompositorFrame
msg(
750 rvh()->GetRoutingID(), 0, frame
, std::vector
<IPC::Message
>());
752 EXPECT_TRUE(swapped_out_rfh
->render_view_host()->OnMessageReceived(msg
));
753 EXPECT_TRUE(swapped_out_rwhv
->did_swap_compositor_frame());
756 // Test if RenderViewHost::GetRenderWidgetHosts() only returns active
758 TEST_F(RenderFrameHostManagerTest
, GetRenderWidgetHostsReturnsActiveViews
) {
759 // This test is invalid in --site-per-process mode, as swapped-out is no
761 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
762 switches::kSitePerProcess
)) {
766 TestRenderFrameHost
* swapped_out_rfh
= CreateSwappedOutRenderFrameHost();
767 EXPECT_TRUE(swapped_out_rfh
->is_swapped_out());
769 scoped_ptr
<RenderWidgetHostIterator
> widgets(
770 RenderWidgetHost::GetRenderWidgetHosts());
771 // We know that there is the only one active widget. Another view is
772 // now swapped out, so the swapped out view is not included in the
774 RenderWidgetHost
* widget
= widgets
->GetNextHost();
775 EXPECT_FALSE(widgets
->GetNextHost());
776 RenderViewHost
* rvh
= RenderViewHost::From(widget
);
777 EXPECT_TRUE(static_cast<RenderViewHostImpl
*>(rvh
)->is_active());
780 // Test if RenderViewHost::GetRenderWidgetHosts() returns a subset of
781 // RenderViewHostImpl::GetAllRenderWidgetHosts().
782 // RenderViewHost::GetRenderWidgetHosts() returns only active widgets, but
783 // RenderViewHostImpl::GetAllRenderWidgetHosts() returns everything
784 // including swapped out ones.
785 TEST_F(RenderFrameHostManagerTest
,
786 GetRenderWidgetHostsWithinGetAllRenderWidgetHosts
) {
787 // This test is invalid in --site-per-process mode, as swapped-out is no
789 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
790 switches::kSitePerProcess
)) {
794 TestRenderFrameHost
* swapped_out_rfh
= CreateSwappedOutRenderFrameHost();
795 EXPECT_TRUE(swapped_out_rfh
->is_swapped_out());
797 scoped_ptr
<RenderWidgetHostIterator
> widgets(
798 RenderWidgetHost::GetRenderWidgetHosts());
800 while (RenderWidgetHost
* w
= widgets
->GetNextHost()) {
802 scoped_ptr
<RenderWidgetHostIterator
> all_widgets(
803 RenderWidgetHostImpl::GetAllRenderWidgetHosts());
804 while (RenderWidgetHost
* widget
= all_widgets
->GetNextHost()) {
814 // Test if SiteInstanceImpl::active_frame_count() is correctly updated
815 // as frames in a SiteInstance get swapped out and in.
816 TEST_F(RenderFrameHostManagerTest
, ActiveFrameCountWhileSwappingInAndOut
) {
817 const GURL
kUrl1("http://www.google.com/");
818 const GURL
kUrl2("http://www.chromium.org/");
820 // Navigate to an initial URL.
821 contents()->NavigateAndCommit(kUrl1
);
822 TestRenderFrameHost
* rfh1
= main_test_rfh();
824 SiteInstanceImpl
* instance1
= rfh1
->GetSiteInstance();
825 EXPECT_EQ(instance1
->active_frame_count(), 1U);
827 // Create 2 new tabs and simulate them being the opener chain for the main
828 // tab. They should be in the same SiteInstance.
829 scoped_ptr
<TestWebContents
> opener1(
830 TestWebContents::Create(browser_context(), instance1
));
831 contents()->SetOpener(opener1
.get());
833 scoped_ptr
<TestWebContents
> opener2(
834 TestWebContents::Create(browser_context(), instance1
));
835 opener1
->SetOpener(opener2
.get());
837 EXPECT_EQ(instance1
->active_frame_count(), 3U);
839 // Navigate to a cross-site URL (different SiteInstance but same
840 // BrowsingInstance).
841 contents()->NavigateAndCommit(kUrl2
);
842 TestRenderFrameHost
* rfh2
= main_test_rfh();
843 SiteInstanceImpl
* instance2
= rfh2
->GetSiteInstance();
845 // rvh2 is on chromium.org which is different from google.com on
846 // which other tabs are.
847 EXPECT_EQ(instance2
->active_frame_count(), 1U);
849 // There are two active views on google.com now.
850 EXPECT_EQ(instance1
->active_frame_count(), 2U);
852 // Navigate to the original origin (google.com).
853 contents()->NavigateAndCommit(kUrl1
);
855 EXPECT_EQ(instance1
->active_frame_count(), 3U);
858 // This deletes a WebContents when the given RVH is deleted. This is
859 // only for testing whether deleting an RVH does not cause any UaF in
860 // other parts of the system. For now, this class is only used for the
861 // next test cases to detect the bug mentioned at
862 // http://crbug.com/259859.
863 class RenderViewHostDestroyer
: public WebContentsObserver
{
865 RenderViewHostDestroyer(RenderViewHost
* render_view_host
,
866 WebContents
* web_contents
)
867 : WebContentsObserver(WebContents::FromRenderViewHost(render_view_host
)),
868 render_view_host_(render_view_host
),
869 web_contents_(web_contents
) {}
871 void RenderViewDeleted(RenderViewHost
* render_view_host
) override
{
872 if (render_view_host
== render_view_host_
)
873 delete web_contents_
;
877 RenderViewHost
* render_view_host_
;
878 WebContents
* web_contents_
;
880 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestroyer
);
883 // Test if ShutdownRenderViewHostsInSiteInstance() does not touch any
884 // RenderWidget that has been freed while deleting a RenderViewHost in
885 // a previous iteration. This is a regression test for
886 // http://crbug.com/259859.
887 TEST_F(RenderFrameHostManagerTest
,
888 DetectUseAfterFreeInShutdownRenderViewHostsInSiteInstance
) {
889 const GURL
kChromeURL("chrome://newtab");
890 const GURL
kUrl1("http://www.google.com");
891 const GURL
kUrl2("http://www.chromium.org");
893 // Navigate our first tab to a chrome url and then to the destination.
894 NavigateActiveAndCommit(kChromeURL
);
895 TestRenderFrameHost
* ntp_rfh
= contents()->GetMainFrame();
897 // Create one more tab and navigate to kUrl1. web_contents is not
898 // wrapped as scoped_ptr since it intentionally deleted by destroyer
899 // below as part of this test.
900 TestWebContents
* web_contents
=
901 TestWebContents::Create(browser_context(), ntp_rfh
->GetSiteInstance());
902 web_contents
->NavigateAndCommit(kUrl1
);
903 RenderViewHostDestroyer
destroyer(ntp_rfh
->GetRenderViewHost(),
906 // This causes the first tab to navigate to kUrl2, which destroys
907 // the ntp_rfh in ShutdownRenderViewHostsInSiteInstance(). When
908 // ntp_rfh is destroyed, it also destroys the RVHs in web_contents
909 // too. This can test whether
910 // SiteInstanceImpl::ShutdownRenderViewHostsInSiteInstance() can
911 // touch any object freed in this way or not while iterating through
913 contents()->NavigateAndCommit(kUrl2
);
916 // When there is an error with the specified page, renderer exits view-source
917 // mode. See WebFrameImpl::DidFail(). We check by this test that
918 // EnableViewSourceMode message is sent on every navigation regardless
919 // RenderView is being newly created or reused.
920 TEST_F(RenderFrameHostManagerTest
, AlwaysSendEnableViewSourceMode
) {
921 const GURL
kChromeUrl("chrome://foo/");
922 const GURL
kUrl("http://foo/");
923 const GURL
kViewSourceUrl("view-source:http://foo/");
925 // We have to navigate to some page at first since without this, the first
926 // navigation will reuse the SiteInstance created by Init(), and the second
927 // one will create a new SiteInstance. Because current_instance and
928 // new_instance will be different, a new RenderViewHost will be created for
929 // the second navigation. We have to avoid this in order to exercise the
931 NavigateActiveAndCommit(kChromeUrl
);
933 // Navigate. Note that "view source" URLs are implemented by putting the RFH
934 // into a view-source mode and then navigating to the inner URL, so that's why
935 // the bare URL is what's committed and returned by the last committed entry's
937 controller().LoadURL(
938 kViewSourceUrl
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
939 int entry_id
= controller().GetPendingEntry()->GetUniqueID();
941 // Simulate response from RenderFrame for DispatchBeforeUnload.
942 contents()->GetMainFrame()->PrepareForCommit();
943 ASSERT_TRUE(contents()->GetPendingMainFrame())
944 << "Expected new pending RenderFrameHost to be created.";
945 RenderFrameHost
* last_rfh
= contents()->GetPendingMainFrame();
947 contents()->GetMaxPageIDForSiteInstance(last_rfh
->GetSiteInstance()) + 1;
948 contents()->GetPendingMainFrame()->SendNavigate(new_id
, entry_id
, true, kUrl
);
950 EXPECT_EQ(1, controller().GetLastCommittedEntryIndex());
951 NavigationEntry
* last_committed
= controller().GetLastCommittedEntry();
952 ASSERT_NE(nullptr, last_committed
);
953 EXPECT_EQ(kUrl
, last_committed
->GetURL());
954 EXPECT_EQ(kViewSourceUrl
, last_committed
->GetVirtualURL());
955 EXPECT_FALSE(controller().GetPendingEntry());
956 // Because we're using TestWebContents and TestRenderViewHost in this
957 // unittest, no one calls WebContentsImpl::RenderViewCreated(). So, we see no
958 // EnableViewSourceMode message, here.
960 // Clear queued messages before load.
961 process()->sink().ClearMessages();
964 controller().LoadURL(
965 kViewSourceUrl
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
966 entry_id
= controller().GetPendingEntry()->GetUniqueID();
967 contents()->GetMainFrame()->PrepareForCommit();
969 // The same RenderViewHost should be reused.
970 EXPECT_FALSE(contents()->GetPendingMainFrame());
971 EXPECT_EQ(last_rfh
, contents()->GetMainFrame());
973 // The renderer sends a commit.
974 contents()->GetMainFrame()->SendNavigateWithTransition(
975 new_id
, entry_id
, false, kUrl
, ui::PAGE_TRANSITION_TYPED
);
976 EXPECT_EQ(1, controller().GetLastCommittedEntryIndex());
977 EXPECT_FALSE(controller().GetPendingEntry());
979 // New message should be sent out to make sure to enter view-source mode.
980 EXPECT_TRUE(process()->sink().GetUniqueMessageMatching(
981 ViewMsg_EnableViewSourceMode::ID
));
984 // Tests the Init function by checking the initial RenderViewHost.
985 TEST_F(RenderFrameHostManagerTest
, Init
) {
986 // Using TestBrowserContext.
987 SiteInstanceImpl
* instance
=
988 static_cast<SiteInstanceImpl
*>(SiteInstance::Create(browser_context()));
989 EXPECT_FALSE(instance
->HasSite());
991 scoped_ptr
<TestWebContents
> web_contents(
992 TestWebContents::Create(browser_context(), instance
));
994 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
995 RenderViewHostImpl
* rvh
= manager
->current_host();
996 RenderFrameHostImpl
* rfh
= manager
->current_frame_host();
999 EXPECT_EQ(rvh
, rfh
->render_view_host());
1000 EXPECT_EQ(instance
, rvh
->GetSiteInstance());
1001 EXPECT_EQ(web_contents
.get(), rvh
->GetDelegate());
1002 EXPECT_EQ(web_contents
.get(), rfh
->delegate());
1003 EXPECT_TRUE(manager
->GetRenderWidgetHostView());
1004 EXPECT_FALSE(manager
->pending_render_view_host());
1007 // Tests the Navigate function. We navigate three sites consecutively and check
1008 // how the pending/committed RenderViewHost are modified.
1009 TEST_F(RenderFrameHostManagerTest
, Navigate
) {
1010 SiteInstance
* instance
= SiteInstance::Create(browser_context());
1012 scoped_ptr
<TestWebContents
> web_contents(
1013 TestWebContents::Create(browser_context(), instance
));
1014 RenderViewHostChangedObserver
change_observer(web_contents
.get());
1016 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
1017 RenderFrameHostImpl
* host
= NULL
;
1019 // 1) The first navigation. --------------------------
1020 const GURL
kUrl1("http://www.google.com/");
1021 NavigationEntryImpl
entry1(
1022 NULL
/* instance */, -1 /* page_id */, kUrl1
, Referrer(),
1023 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED
,
1024 false /* is_renderer_init */);
1025 host
= NavigateToEntry(manager
, entry1
);
1027 // The RenderFrameHost created in Init will be reused.
1028 EXPECT_TRUE(host
== manager
->current_frame_host());
1029 EXPECT_FALSE(GetPendingFrameHost(manager
));
1032 manager
->DidNavigateFrame(host
, true);
1033 // Commit to SiteInstance should be delayed until RenderFrame commit.
1034 EXPECT_TRUE(host
== manager
->current_frame_host());
1036 EXPECT_FALSE(host
->GetSiteInstance()->HasSite());
1037 host
->GetSiteInstance()->SetSite(kUrl1
);
1039 // 2) Navigate to next site. -------------------------
1040 const GURL
kUrl2("http://www.google.com/foo");
1041 NavigationEntryImpl
entry2(
1042 NULL
/* instance */, -1 /* page_id */, kUrl2
,
1043 Referrer(kUrl1
, blink::WebReferrerPolicyDefault
),
1044 base::string16() /* title */, ui::PAGE_TRANSITION_LINK
,
1045 true /* is_renderer_init */);
1046 host
= NavigateToEntry(manager
, entry2
);
1048 // The RenderFrameHost created in Init will be reused.
1049 EXPECT_TRUE(host
== manager
->current_frame_host());
1050 EXPECT_FALSE(GetPendingFrameHost(manager
));
1053 manager
->DidNavigateFrame(host
, true);
1054 EXPECT_TRUE(host
== manager
->current_frame_host());
1056 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
1058 // 3) Cross-site navigate to next site. --------------
1059 const GURL
kUrl3("http://webkit.org/");
1060 NavigationEntryImpl
entry3(
1061 NULL
/* instance */, -1 /* page_id */, kUrl3
,
1062 Referrer(kUrl2
, blink::WebReferrerPolicyDefault
),
1063 base::string16() /* title */, ui::PAGE_TRANSITION_LINK
,
1064 false /* is_renderer_init */);
1065 host
= NavigateToEntry(manager
, entry3
);
1067 // A new RenderFrameHost should be created.
1068 EXPECT_TRUE(GetPendingFrameHost(manager
));
1069 ASSERT_EQ(host
, GetPendingFrameHost(manager
));
1071 change_observer
.Reset();
1074 manager
->DidNavigateFrame(GetPendingFrameHost(manager
), true);
1075 EXPECT_TRUE(host
== manager
->current_frame_host());
1077 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
1078 // Check the pending RenderFrameHost has been committed.
1079 EXPECT_FALSE(GetPendingFrameHost(manager
));
1081 // We should observe RVH changed event.
1082 EXPECT_TRUE(change_observer
.DidHostChange());
1085 // Tests WebUI creation.
1086 TEST_F(RenderFrameHostManagerTest
, WebUI
) {
1087 set_should_create_webui(true);
1088 SiteInstance
* instance
= SiteInstance::Create(browser_context());
1090 scoped_ptr
<TestWebContents
> web_contents(
1091 TestWebContents::Create(browser_context(), instance
));
1092 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
1093 RenderFrameHostImpl
* initial_rfh
= manager
->current_frame_host();
1095 EXPECT_FALSE(manager
->current_host()->IsRenderViewLive());
1096 EXPECT_FALSE(manager
->web_ui());
1097 EXPECT_TRUE(initial_rfh
);
1099 const GURL
kUrl("chrome://foo");
1100 NavigationEntryImpl
entry(NULL
/* instance */, -1 /* page_id */, kUrl
,
1101 Referrer(), base::string16() /* title */,
1102 ui::PAGE_TRANSITION_TYPED
,
1103 false /* is_renderer_init */);
1104 RenderFrameHostImpl
* host
= NavigateToEntry(manager
, entry
);
1106 // We commit the pending RenderFrameHost immediately because the previous
1107 // RenderFrameHost was not live. We test a case where it is live in
1110 EXPECT_NE(initial_rfh
, host
);
1111 EXPECT_EQ(host
, manager
->current_frame_host());
1112 EXPECT_FALSE(GetPendingFrameHost(manager
));
1114 // It's important that the SiteInstance get set on the Web UI page as soon
1115 // as the navigation starts, rather than lazily after it commits, so we don't
1116 // try to re-use the SiteInstance/process for non Web UI things that may
1117 // get loaded in between.
1118 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
1119 EXPECT_EQ(kUrl
, host
->GetSiteInstance()->GetSiteURL());
1121 // The Web UI is committed immediately because the RenderViewHost has not been
1122 // used yet. UpdateStateForNavigate() took the short cut path.
1123 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1124 switches::kEnableBrowserSideNavigation
)) {
1125 EXPECT_FALSE(manager
->speculative_web_ui());
1127 EXPECT_FALSE(manager
->pending_web_ui());
1129 EXPECT_TRUE(manager
->web_ui());
1132 manager
->DidNavigateFrame(host
, true);
1134 host
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1137 // Tests that we can open a WebUI link in a new tab from a WebUI page and still
1138 // grant the correct bindings. http://crbug.com/189101.
1139 TEST_F(RenderFrameHostManagerTest
, WebUIInNewTab
) {
1140 set_should_create_webui(true);
1141 SiteInstance
* blank_instance
= SiteInstance::Create(browser_context());
1142 blank_instance
->GetProcess()->Init();
1144 // Create a blank tab.
1145 scoped_ptr
<TestWebContents
> web_contents1(
1146 TestWebContents::Create(browser_context(), blank_instance
));
1147 RenderFrameHostManager
* manager1
=
1148 web_contents1
->GetRenderManagerForTesting();
1149 // Test the case that new RVH is considered live.
1150 manager1
->current_host()->CreateRenderView(
1151 base::string16(), -1, MSG_ROUTING_NONE
, -1,
1152 FrameReplicationState(), false);
1153 EXPECT_TRUE(manager1
->current_host()->IsRenderViewLive());
1154 EXPECT_TRUE(manager1
->current_frame_host()->IsRenderFrameLive());
1156 // Navigate to a WebUI page.
1157 const GURL
kUrl1("chrome://foo");
1158 NavigationEntryImpl
entry1(NULL
/* instance */, -1 /* page_id */, kUrl1
,
1159 Referrer(), base::string16() /* title */,
1160 ui::PAGE_TRANSITION_TYPED
,
1161 false /* is_renderer_init */);
1162 RenderFrameHostImpl
* host1
= NavigateToEntry(manager1
, entry1
);
1164 // We should have a pending navigation to the WebUI RenderViewHost.
1165 // It should already have bindings.
1166 EXPECT_EQ(host1
, GetPendingFrameHost(manager1
));
1167 EXPECT_NE(host1
, manager1
->current_frame_host());
1169 host1
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1171 // Commit and ensure we still have bindings.
1172 manager1
->DidNavigateFrame(host1
, true);
1173 SiteInstance
* webui_instance
= host1
->GetSiteInstance();
1174 EXPECT_EQ(host1
, manager1
->current_frame_host());
1176 host1
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1178 // Now simulate clicking a link that opens in a new tab.
1179 scoped_ptr
<TestWebContents
> web_contents2(
1180 TestWebContents::Create(browser_context(), webui_instance
));
1181 RenderFrameHostManager
* manager2
=
1182 web_contents2
->GetRenderManagerForTesting();
1183 // Make sure the new RVH is considered live. This is usually done in
1184 // RenderWidgetHost::Init when opening a new tab from a link.
1185 manager2
->current_host()->CreateRenderView(
1186 base::string16(), -1, MSG_ROUTING_NONE
, -1,
1187 FrameReplicationState(), false);
1188 EXPECT_TRUE(manager2
->current_host()->IsRenderViewLive());
1190 const GURL
kUrl2("chrome://foo/bar");
1191 NavigationEntryImpl
entry2(NULL
/* instance */, -1 /* page_id */, kUrl2
,
1192 Referrer(), base::string16() /* title */,
1193 ui::PAGE_TRANSITION_LINK
,
1194 true /* is_renderer_init */);
1195 RenderFrameHostImpl
* host2
= NavigateToEntry(manager2
, entry2
);
1197 // No cross-process transition happens because we are already in the right
1198 // SiteInstance. We should grant bindings immediately.
1199 EXPECT_EQ(host2
, manager2
->current_frame_host());
1200 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1201 switches::kEnableBrowserSideNavigation
)) {
1202 EXPECT_TRUE(manager2
->speculative_web_ui());
1204 EXPECT_TRUE(manager2
->pending_web_ui());
1207 host2
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1209 manager2
->DidNavigateFrame(host2
, true);
1212 // Tests that a WebUI is correctly reused between chrome:// pages.
1213 TEST_F(RenderFrameHostManagerTest
, WebUIWasReused
) {
1214 set_should_create_webui(true);
1216 // Navigate to a WebUI page.
1217 const GURL
kUrl1("chrome://foo");
1218 contents()->NavigateAndCommit(kUrl1
);
1219 RenderFrameHostManager
* manager
=
1220 main_test_rfh()->frame_tree_node()->render_manager();
1221 WebUIImpl
* web_ui
= manager
->web_ui();
1222 EXPECT_TRUE(web_ui
);
1224 // Navigate to another WebUI page which should be same-site and keep the
1226 const GURL
kUrl2("chrome://foo/bar");
1227 contents()->NavigateAndCommit(kUrl2
);
1228 EXPECT_EQ(web_ui
, manager
->web_ui());
1231 // Tests that a WebUI is correctly cleaned up when navigating from a chrome://
1232 // page to a non-chrome:// page.
1233 TEST_F(RenderFrameHostManagerTest
, WebUIWasCleared
) {
1234 set_should_create_webui(true);
1236 // Navigate to a WebUI page.
1237 const GURL
kUrl1("chrome://foo");
1238 contents()->NavigateAndCommit(kUrl1
);
1239 EXPECT_TRUE(main_test_rfh()->frame_tree_node()->render_manager()->web_ui());
1241 // Navigate to a non-WebUI page.
1242 const GURL
kUrl2("http://www.google.com");
1243 contents()->NavigateAndCommit(kUrl2
);
1244 EXPECT_FALSE(main_test_rfh()->frame_tree_node()->render_manager()->web_ui());
1247 // Tests that we don't end up in an inconsistent state if a page does a back and
1248 // then reload. http://crbug.com/51680
1249 // Also tests that only user-gesture navigations can interrupt cross-process
1250 // navigations. http://crbug.com/75195
1251 TEST_F(RenderFrameHostManagerTest
, PageDoesBackAndReload
) {
1252 const GURL
kUrl1("http://www.google.com/");
1253 const GURL
kUrl2("http://www.evil-site.com/");
1255 // Navigate to a safe site, then an evil site.
1256 // This will switch RenderFrameHosts. We cannot assert that the first and
1257 // second RFHs are different, though, because the first one may be promptly
1259 contents()->NavigateAndCommit(kUrl1
);
1260 contents()->NavigateAndCommit(kUrl2
);
1261 TestRenderFrameHost
* evil_rfh
= contents()->GetMainFrame();
1263 // Now let's simulate the evil page calling history.back().
1264 contents()->OnGoToEntryAtOffset(-1);
1265 contents()->GetMainFrame()->PrepareForCommit();
1266 // We should have a new pending RFH.
1267 // Note that in this case, the navigation has not committed, so evil_rfh will
1268 // not be deleted yet.
1269 EXPECT_NE(evil_rfh
, contents()->GetPendingMainFrame());
1270 EXPECT_NE(evil_rfh
->GetRenderViewHost(),
1271 contents()->GetPendingMainFrame()->GetRenderViewHost());
1273 // Before that RFH has committed, the evil page reloads itself.
1274 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1276 params
.nav_entry_id
= 0;
1277 params
.did_create_new_entry
= false;
1279 params
.transition
= ui::PAGE_TRANSITION_CLIENT_REDIRECT
;
1280 params
.should_update_history
= false;
1281 params
.gesture
= NavigationGestureAuto
;
1282 params
.was_within_same_page
= false;
1283 params
.is_post
= false;
1284 params
.page_state
= PageState::CreateFromURL(kUrl2
);
1286 contents()->GetFrameTree()->root()->navigator()->DidNavigate(evil_rfh
,
1289 // That should NOT have cancelled the pending RFH, because the reload did
1290 // not have a user gesture. Thus, the pending back navigation will still
1291 // eventually commit.
1292 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
1293 pending_render_view_host() != NULL
);
1294 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->pending_frame_host() !=
1297 contents()->GetRenderManagerForTesting()->current_frame_host());
1298 EXPECT_EQ(evil_rfh
->GetRenderViewHost(),
1299 contents()->GetRenderManagerForTesting()->current_host());
1301 // Also we should not have a pending navigation entry.
1302 EXPECT_TRUE(contents()->GetController().GetPendingEntry() == NULL
);
1303 NavigationEntry
* entry
= contents()->GetController().GetVisibleEntry();
1304 ASSERT_TRUE(entry
!= NULL
);
1305 EXPECT_EQ(kUrl2
, entry
->GetURL());
1307 // Now do the same but as a user gesture.
1308 params
.gesture
= NavigationGestureUser
;
1309 contents()->GetFrameTree()->root()->navigator()->DidNavigate(evil_rfh
,
1312 // User navigation should have cancelled the pending RFH.
1313 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
1314 pending_render_view_host() == NULL
);
1315 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->pending_frame_host() ==
1318 contents()->GetRenderManagerForTesting()->current_frame_host());
1319 EXPECT_EQ(evil_rfh
->GetRenderViewHost(),
1320 contents()->GetRenderManagerForTesting()->current_host());
1322 // Also we should not have a pending navigation entry.
1323 EXPECT_TRUE(contents()->GetController().GetPendingEntry() == NULL
);
1324 entry
= contents()->GetController().GetVisibleEntry();
1325 ASSERT_TRUE(entry
!= NULL
);
1326 EXPECT_EQ(kUrl2
, entry
->GetURL());
1329 // Ensure that we can go back and forward even if a SwapOut ACK isn't received.
1330 // See http://crbug.com/93427.
1331 TEST_F(RenderFrameHostManagerTest
, NavigateAfterMissingSwapOutACK
) {
1332 const GURL
kUrl1("http://www.google.com/");
1333 const GURL
kUrl2("http://www.chromium.org/");
1335 // Navigate to two pages.
1336 contents()->NavigateAndCommit(kUrl1
);
1337 TestRenderFrameHost
* rfh1
= main_test_rfh();
1339 // Keep active_frame_count nonzero so that no swapped out frames in
1340 // this SiteInstance get forcefully deleted.
1341 rfh1
->GetSiteInstance()->increment_active_frame_count();
1343 contents()->NavigateAndCommit(kUrl2
);
1344 TestRenderFrameHost
* rfh2
= main_test_rfh();
1345 rfh2
->GetSiteInstance()->increment_active_frame_count();
1347 // Now go back, but suppose the SwapOut_ACK isn't received. This shouldn't
1348 // happen, but we have seen it when going back quickly across many entries
1349 // (http://crbug.com/93427).
1350 contents()->GetController().GoBack();
1351 EXPECT_TRUE(rfh2
->IsWaitingForBeforeUnloadACK());
1352 contents()->GetMainFrame()->PrepareForCommit();
1353 EXPECT_FALSE(rfh2
->IsWaitingForBeforeUnloadACK());
1355 // The back navigation commits.
1356 const NavigationEntry
* entry1
= contents()->GetController().GetPendingEntry();
1357 contents()->GetPendingMainFrame()->SendNavigate(
1358 entry1
->GetPageID(), entry1
->GetUniqueID(), false, entry1
->GetURL());
1359 EXPECT_TRUE(rfh2
->IsWaitingForUnloadACK());
1360 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh2
->rfh_state());
1362 // We should be able to navigate forward.
1363 contents()->GetController().GoForward();
1364 contents()->GetMainFrame()->PrepareForCommit();
1365 const NavigationEntry
* entry2
= contents()->GetController().GetPendingEntry();
1366 contents()->GetPendingMainFrame()->SendNavigate(
1367 entry2
->GetPageID(), entry2
->GetUniqueID(), false, entry2
->GetURL());
1368 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, main_test_rfh()->rfh_state());
1369 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
1370 switches::kSitePerProcess
)) {
1371 EXPECT_EQ(rfh2
, main_test_rfh());
1372 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
1373 rfh1
->OnSwappedOut();
1374 EXPECT_TRUE(rfh1
->is_swapped_out());
1375 EXPECT_EQ(RenderFrameHostImpl::STATE_SWAPPED_OUT
, rfh1
->rfh_state());
1379 // Test that we create swapped out RFHs for the opener chain when navigating an
1380 // opened tab cross-process. This allows us to support certain cross-process
1381 // JavaScript calls (http://crbug.com/99202).
1382 TEST_F(RenderFrameHostManagerTest
, CreateSwappedOutOpenerRFHs
) {
1383 const GURL
kUrl1("http://www.google.com/");
1384 const GURL
kUrl2("http://www.chromium.org/");
1385 const GURL
kChromeUrl("chrome://foo");
1386 bool is_site_per_process
= base::CommandLine::ForCurrentProcess()->HasSwitch(
1387 switches::kSitePerProcess
);
1389 // Navigate to an initial URL.
1390 contents()->NavigateAndCommit(kUrl1
);
1391 RenderFrameHostManager
* manager
= contents()->GetRenderManagerForTesting();
1392 TestRenderFrameHost
* rfh1
= main_test_rfh();
1393 scoped_refptr
<SiteInstanceImpl
> site_instance1
= rfh1
->GetSiteInstance();
1394 RenderFrameHostDeletedObserver
rfh1_deleted_observer(rfh1
);
1395 TestRenderViewHost
* rvh1
= test_rvh();
1397 // Create 2 new tabs and simulate them being the opener chain for the main
1398 // tab. They should be in the same SiteInstance.
1399 scoped_ptr
<TestWebContents
> opener1(
1400 TestWebContents::Create(browser_context(), site_instance1
.get()));
1401 RenderFrameHostManager
* opener1_manager
=
1402 opener1
->GetRenderManagerForTesting();
1403 contents()->SetOpener(opener1
.get());
1405 scoped_ptr
<TestWebContents
> opener2(
1406 TestWebContents::Create(browser_context(), site_instance1
.get()));
1407 RenderFrameHostManager
* opener2_manager
=
1408 opener2
->GetRenderManagerForTesting();
1409 opener1
->SetOpener(opener2
.get());
1411 // Navigate to a cross-site URL (different SiteInstance but same
1412 // BrowsingInstance).
1413 contents()->NavigateAndCommit(kUrl2
);
1414 TestRenderFrameHost
* rfh2
= main_test_rfh();
1415 TestRenderViewHost
* rvh2
= test_rvh();
1416 EXPECT_NE(site_instance1
, rfh2
->GetSiteInstance());
1417 EXPECT_TRUE(site_instance1
->IsRelatedSiteInstance(rfh2
->GetSiteInstance()));
1419 // Ensure rvh1 is placed on swapped out list of the current tab.
1420 if (!is_site_per_process
) {
1421 EXPECT_TRUE(manager
->IsRVHOnSwappedOutList(rvh1
));
1422 EXPECT_FALSE(rfh1_deleted_observer
.deleted());
1423 EXPECT_TRUE(manager
->IsOnSwappedOutList(rfh1
));
1425 manager
->GetRenderFrameProxyHost(site_instance1
.get())
1426 ->render_frame_host());
1428 EXPECT_TRUE(rfh1_deleted_observer
.deleted());
1429 EXPECT_TRUE(manager
->GetRenderFrameProxyHost(site_instance1
.get()));
1432 manager
->GetSwappedOutRenderViewHost(rvh1
->GetSiteInstance()));
1434 // Ensure a swapped out RFH and RFH is created in the first opener tab.
1435 RenderFrameProxyHost
* opener1_proxy
=
1436 opener1_manager
->GetRenderFrameProxyHost(rfh2
->GetSiteInstance());
1437 RenderFrameHostImpl
* opener1_rfh
= opener1_proxy
->render_frame_host();
1438 TestRenderViewHost
* opener1_rvh
= static_cast<TestRenderViewHost
*>(
1439 opener1_manager
->GetSwappedOutRenderViewHost(rvh2
->GetSiteInstance()));
1440 if (!is_site_per_process
) {
1441 EXPECT_TRUE(opener1_manager
->IsOnSwappedOutList(opener1_rfh
));
1442 EXPECT_TRUE(opener1_manager
->IsRVHOnSwappedOutList(opener1_rvh
));
1443 EXPECT_TRUE(opener1_rfh
->is_swapped_out());
1445 EXPECT_FALSE(opener1_rfh
);
1447 EXPECT_FALSE(opener1_rvh
->is_active());
1449 // Ensure a swapped out RFH and RVH is created in the second opener tab.
1450 RenderFrameProxyHost
* opener2_proxy
=
1451 opener2_manager
->GetRenderFrameProxyHost(rfh2
->GetSiteInstance());
1452 RenderFrameHostImpl
* opener2_rfh
= opener2_proxy
->render_frame_host();
1453 TestRenderViewHost
* opener2_rvh
= static_cast<TestRenderViewHost
*>(
1454 opener2_manager
->GetSwappedOutRenderViewHost(rvh2
->GetSiteInstance()));
1455 if (!is_site_per_process
) {
1456 EXPECT_TRUE(opener2_manager
->IsOnSwappedOutList(opener2_rfh
));
1457 EXPECT_TRUE(opener2_manager
->IsRVHOnSwappedOutList(opener2_rvh
));
1458 EXPECT_TRUE(opener2_rfh
->is_swapped_out());
1460 EXPECT_FALSE(opener2_rfh
);
1462 EXPECT_FALSE(opener2_rvh
->is_active());
1464 // Navigate to a cross-BrowsingInstance URL.
1465 contents()->NavigateAndCommit(kChromeUrl
);
1466 TestRenderFrameHost
* rfh3
= main_test_rfh();
1467 EXPECT_NE(site_instance1
, rfh3
->GetSiteInstance());
1468 EXPECT_FALSE(site_instance1
->IsRelatedSiteInstance(rfh3
->GetSiteInstance()));
1470 // No scripting is allowed across BrowsingInstances, so we should not create
1471 // swapped out RVHs for the opener chain in this case.
1472 EXPECT_FALSE(opener1_manager
->GetRenderFrameProxyHost(
1473 rfh3
->GetSiteInstance()));
1474 EXPECT_FALSE(opener1_manager
->GetSwappedOutRenderViewHost(
1475 rfh3
->GetSiteInstance()));
1476 EXPECT_FALSE(opener2_manager
->GetRenderFrameProxyHost(
1477 rfh3
->GetSiteInstance()));
1478 EXPECT_FALSE(opener2_manager
->GetSwappedOutRenderViewHost(
1479 rfh3
->GetSiteInstance()));
1482 // Test that a page can disown the opener of the WebContents.
1483 TEST_F(RenderFrameHostManagerTest
, DisownOpener
) {
1484 const GURL
kUrl1("http://www.google.com/");
1485 const GURL
kUrl2("http://www.chromium.org/");
1487 // Navigate to an initial URL.
1488 contents()->NavigateAndCommit(kUrl1
);
1489 TestRenderFrameHost
* rfh1
= main_test_rfh();
1490 scoped_refptr
<SiteInstanceImpl
> site_instance1
= rfh1
->GetSiteInstance();
1492 // Create a new tab and simulate having it be the opener for the main tab.
1493 scoped_ptr
<TestWebContents
> opener1(
1494 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1495 contents()->SetOpener(opener1
.get());
1496 EXPECT_TRUE(contents()->HasOpener());
1498 // Navigate to a cross-site URL (different SiteInstance but same
1499 // BrowsingInstance).
1500 contents()->NavigateAndCommit(kUrl2
);
1501 TestRenderFrameHost
* rfh2
= main_test_rfh();
1502 EXPECT_NE(site_instance1
, rfh2
->GetSiteInstance());
1504 // Disown the opener from rfh2.
1505 rfh2
->DidDisownOpener();
1507 // Ensure the opener is cleared.
1508 EXPECT_FALSE(contents()->HasOpener());
1511 // Test that a page can disown a same-site opener of the WebContents.
1512 TEST_F(RenderFrameHostManagerTest
, DisownSameSiteOpener
) {
1513 const GURL
kUrl1("http://www.google.com/");
1515 // Navigate to an initial URL.
1516 contents()->NavigateAndCommit(kUrl1
);
1517 TestRenderFrameHost
* rfh1
= main_test_rfh();
1519 // Create a new tab and simulate having it be the opener for the main tab.
1520 scoped_ptr
<TestWebContents
> opener1(
1521 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1522 contents()->SetOpener(opener1
.get());
1523 EXPECT_TRUE(contents()->HasOpener());
1525 // Disown the opener from rfh1.
1526 rfh1
->DidDisownOpener();
1528 // Ensure the opener is cleared even if it is in the same process.
1529 EXPECT_FALSE(contents()->HasOpener());
1532 // Test that a page can disown the opener just as a cross-process navigation is
1534 TEST_F(RenderFrameHostManagerTest
, DisownOpenerDuringNavigation
) {
1535 const GURL
kUrl1("http://www.google.com/");
1536 const GURL
kUrl2("http://www.chromium.org/");
1538 // Navigate to an initial URL.
1539 contents()->NavigateAndCommit(kUrl1
);
1540 scoped_refptr
<SiteInstanceImpl
> site_instance1
=
1541 main_test_rfh()->GetSiteInstance();
1543 // Create a new tab and simulate having it be the opener for the main tab.
1544 scoped_ptr
<TestWebContents
> opener1(
1545 TestWebContents::Create(browser_context(), site_instance1
.get()));
1546 contents()->SetOpener(opener1
.get());
1547 EXPECT_TRUE(contents()->HasOpener());
1549 // Navigate to a cross-site URL (different SiteInstance but same
1550 // BrowsingInstance).
1551 contents()->NavigateAndCommit(kUrl2
);
1552 TestRenderFrameHost
* rfh2
= main_test_rfh();
1553 EXPECT_NE(site_instance1
, rfh2
->GetSiteInstance());
1555 // Start a back navigation.
1556 contents()->GetController().GoBack();
1557 contents()->GetMainFrame()->PrepareForCommit();
1559 // Disown the opener from rfh2.
1560 rfh2
->DidDisownOpener();
1562 // Ensure the opener is cleared.
1563 EXPECT_FALSE(contents()->HasOpener());
1565 // The back navigation commits.
1566 const NavigationEntry
* entry1
= contents()->GetController().GetPendingEntry();
1567 contents()->GetPendingMainFrame()->SendNavigate(
1568 entry1
->GetPageID(), entry1
->GetUniqueID(), false, entry1
->GetURL());
1570 // Ensure the opener is still cleared.
1571 EXPECT_FALSE(contents()->HasOpener());
1574 // Test that a page can disown the opener just after a cross-process navigation
1576 TEST_F(RenderFrameHostManagerTest
, DisownOpenerAfterNavigation
) {
1577 const GURL
kUrl1("http://www.google.com/");
1578 const GURL
kUrl2("http://www.chromium.org/");
1580 // Navigate to an initial URL.
1581 contents()->NavigateAndCommit(kUrl1
);
1582 scoped_refptr
<SiteInstanceImpl
> site_instance1
=
1583 main_test_rfh()->GetSiteInstance();
1585 // Create a new tab and simulate having it be the opener for the main tab.
1586 scoped_ptr
<TestWebContents
> opener1(
1587 TestWebContents::Create(browser_context(), site_instance1
.get()));
1588 contents()->SetOpener(opener1
.get());
1589 EXPECT_TRUE(contents()->HasOpener());
1591 // Navigate to a cross-site URL (different SiteInstance but same
1592 // BrowsingInstance).
1593 contents()->NavigateAndCommit(kUrl2
);
1594 TestRenderFrameHost
* rfh2
= main_test_rfh();
1595 EXPECT_NE(site_instance1
, rfh2
->GetSiteInstance());
1597 // Commit a back navigation before the DidDisownOpener message arrives.
1598 contents()->GetController().GoBack();
1599 contents()->GetMainFrame()->PrepareForCommit();
1600 const NavigationEntry
* entry1
= contents()->GetController().GetPendingEntry();
1601 contents()->GetPendingMainFrame()->SendNavigate(
1602 entry1
->GetPageID(), entry1
->GetUniqueID(), false, entry1
->GetURL());
1604 // Disown the opener from rfh2.
1605 rfh2
->DidDisownOpener();
1606 EXPECT_FALSE(contents()->HasOpener());
1609 // Test that we clean up swapped out RenderViewHosts when a process hosting
1610 // those associated RenderViews crashes. http://crbug.com/258993
1611 TEST_F(RenderFrameHostManagerTest
, CleanUpSwappedOutRVHOnProcessCrash
) {
1612 const GURL
kUrl1("http://www.google.com/");
1613 const GURL
kUrl2("http://www.chromium.org/");
1615 // Navigate to an initial URL.
1616 contents()->NavigateAndCommit(kUrl1
);
1617 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1619 // Create a new tab as an opener for the main tab.
1620 scoped_ptr
<TestWebContents
> opener1(
1621 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1622 RenderFrameHostManager
* opener1_manager
=
1623 opener1
->GetRenderManagerForTesting();
1624 contents()->SetOpener(opener1
.get());
1626 // Make sure the new opener RVH is considered live.
1627 opener1_manager
->current_host()->CreateRenderView(
1628 base::string16(), -1, MSG_ROUTING_NONE
, -1,
1629 FrameReplicationState(), false);
1630 EXPECT_TRUE(opener1_manager
->current_host()->IsRenderViewLive());
1631 EXPECT_TRUE(opener1_manager
->current_frame_host()->IsRenderFrameLive());
1633 // Use a cross-process navigation in the opener to swap out the old RVH.
1635 opener1_manager
->GetSwappedOutRenderViewHost(rfh1
->GetSiteInstance()));
1636 opener1
->NavigateAndCommit(kUrl2
);
1638 opener1_manager
->GetSwappedOutRenderViewHost(rfh1
->GetSiteInstance()));
1640 // Fake a process crash.
1641 rfh1
->GetProcess()->SimulateCrash();
1643 // Ensure that the RenderFrameProxyHost stays around and the RenderFrameProxy
1645 RenderFrameProxyHost
* render_frame_proxy_host
=
1646 opener1_manager
->GetRenderFrameProxyHost(rfh1
->GetSiteInstance());
1647 EXPECT_TRUE(render_frame_proxy_host
);
1648 EXPECT_FALSE(render_frame_proxy_host
->is_render_frame_proxy_live());
1650 // Expect the swapped out RVH to exist.
1652 opener1_manager
->GetSwappedOutRenderViewHost(rfh1
->GetSiteInstance()));
1654 // Reload the initial tab. This should recreate the opener's swapped out RVH
1655 // in the original SiteInstance.
1656 contents()->GetController().Reload(true);
1657 contents()->GetMainFrame()->PrepareForCommit();
1658 EXPECT_EQ(opener1_manager
->GetSwappedOutRenderViewHost(
1659 rfh1
->GetSiteInstance())->GetRoutingID(),
1660 contents()->GetMainFrame()->GetRenderViewHost()->opener_route_id());
1663 // Test that RenderViewHosts created for WebUI navigations are properly
1664 // granted WebUI bindings even if an unprivileged swapped out RenderViewHost
1665 // is in the same process (http://crbug.com/79918).
1666 TEST_F(RenderFrameHostManagerTest
, EnableWebUIWithSwappedOutOpener
) {
1667 set_should_create_webui(true);
1668 const GURL
kSettingsUrl("chrome://chrome/settings");
1669 const GURL
kPluginUrl("chrome://plugins");
1671 // Navigate to an initial WebUI URL.
1672 contents()->NavigateAndCommit(kSettingsUrl
);
1674 // Ensure the RVH has WebUI bindings.
1675 TestRenderViewHost
* rvh1
= test_rvh();
1676 EXPECT_TRUE(rvh1
->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1678 // Create a new tab and simulate it being the opener for the main
1679 // tab. It should be in the same SiteInstance.
1680 scoped_ptr
<TestWebContents
> opener1(
1681 TestWebContents::Create(browser_context(), rvh1
->GetSiteInstance()));
1682 RenderFrameHostManager
* opener1_manager
=
1683 opener1
->GetRenderManagerForTesting();
1684 contents()->SetOpener(opener1
.get());
1686 // Navigate to a different WebUI URL (different SiteInstance, same
1687 // BrowsingInstance).
1688 contents()->NavigateAndCommit(kPluginUrl
);
1689 TestRenderViewHost
* rvh2
= test_rvh();
1690 EXPECT_NE(rvh1
->GetSiteInstance(), rvh2
->GetSiteInstance());
1691 EXPECT_TRUE(rvh1
->GetSiteInstance()->IsRelatedSiteInstance(
1692 rvh2
->GetSiteInstance()));
1694 // Ensure a swapped out RFH and RVH is created in the first opener tab.
1695 RenderFrameProxyHost
* opener1_proxy
=
1696 opener1_manager
->GetRenderFrameProxyHost(rvh2
->GetSiteInstance());
1697 RenderFrameHostImpl
* opener1_rfh
= opener1_proxy
->render_frame_host();
1698 TestRenderViewHost
* opener1_rvh
= static_cast<TestRenderViewHost
*>(
1699 opener1_manager
->GetSwappedOutRenderViewHost(rvh2
->GetSiteInstance()));
1700 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
1701 switches::kSitePerProcess
)) {
1702 EXPECT_TRUE(opener1_manager
->IsOnSwappedOutList(opener1_rfh
));
1703 EXPECT_TRUE(opener1_manager
->IsRVHOnSwappedOutList(opener1_rvh
));
1704 EXPECT_TRUE(opener1_rfh
->is_swapped_out());
1706 EXPECT_FALSE(opener1_rfh
);
1708 EXPECT_FALSE(opener1_rvh
->is_active());
1710 // Ensure the new RVH has WebUI bindings.
1711 EXPECT_TRUE(rvh2
->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1714 // Test that we reuse the same guest SiteInstance if we navigate across sites.
1715 TEST_F(RenderFrameHostManagerTest
, NoSwapOnGuestNavigations
) {
1716 GURL
guest_url(std::string(kGuestScheme
).append("://abc123"));
1717 SiteInstance
* instance
=
1718 SiteInstance::CreateForURL(browser_context(), guest_url
);
1719 scoped_ptr
<TestWebContents
> web_contents(
1720 TestWebContents::Create(browser_context(), instance
));
1722 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
1724 RenderFrameHostImpl
* host
= NULL
;
1726 // 1) The first navigation. --------------------------
1727 const GURL
kUrl1("http://www.google.com/");
1728 NavigationEntryImpl
entry1(
1729 NULL
/* instance */, -1 /* page_id */, kUrl1
, Referrer(),
1730 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED
,
1731 false /* is_renderer_init */);
1732 host
= NavigateToEntry(manager
, entry1
);
1734 // The RenderFrameHost created in Init will be reused.
1735 EXPECT_TRUE(host
== manager
->current_frame_host());
1736 EXPECT_FALSE(manager
->pending_frame_host());
1737 EXPECT_EQ(manager
->current_frame_host()->GetSiteInstance(), instance
);
1740 manager
->DidNavigateFrame(host
, true);
1741 // Commit to SiteInstance should be delayed until RenderFrame commit.
1742 EXPECT_EQ(host
, manager
->current_frame_host());
1744 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
1746 // 2) Navigate to a different domain. -------------------------
1747 // Guests stay in the same process on navigation.
1748 const GURL
kUrl2("http://www.chromium.org");
1749 NavigationEntryImpl
entry2(
1750 NULL
/* instance */, -1 /* page_id */, kUrl2
,
1751 Referrer(kUrl1
, blink::WebReferrerPolicyDefault
),
1752 base::string16() /* title */, ui::PAGE_TRANSITION_LINK
,
1753 true /* is_renderer_init */);
1754 host
= NavigateToEntry(manager
, entry2
);
1756 // The RenderFrameHost created in Init will be reused.
1757 EXPECT_EQ(host
, manager
->current_frame_host());
1758 EXPECT_FALSE(manager
->pending_frame_host());
1761 manager
->DidNavigateFrame(host
, true);
1762 EXPECT_EQ(host
, manager
->current_frame_host());
1764 EXPECT_EQ(host
->GetSiteInstance(), instance
);
1767 // Test that we cancel a pending RVH if we close the tab while it's pending.
1768 // http://crbug.com/294697.
1769 TEST_F(RenderFrameHostManagerTest
, NavigateWithEarlyClose
) {
1770 TestNotificationTracker notifications
;
1772 SiteInstance
* instance
= SiteInstance::Create(browser_context());
1774 BeforeUnloadFiredWebContentsDelegate delegate
;
1775 scoped_ptr
<TestWebContents
> web_contents(
1776 TestWebContents::Create(browser_context(), instance
));
1777 RenderViewHostChangedObserver
change_observer(web_contents
.get());
1778 web_contents
->SetDelegate(&delegate
);
1780 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
1782 // 1) The first navigation. --------------------------
1783 const GURL
kUrl1("http://www.google.com/");
1784 NavigationEntryImpl
entry1(NULL
/* instance */, -1 /* page_id */, kUrl1
,
1785 Referrer(), base::string16() /* title */,
1786 ui::PAGE_TRANSITION_TYPED
,
1787 false /* is_renderer_init */);
1788 RenderFrameHostImpl
* host
= NavigateToEntry(manager
, entry1
);
1790 // The RenderFrameHost created in Init will be reused.
1791 EXPECT_EQ(host
, manager
->current_frame_host());
1792 EXPECT_FALSE(GetPendingFrameHost(manager
));
1794 // We should observe RVH changed event.
1795 EXPECT_TRUE(change_observer
.DidHostChange());
1798 manager
->DidNavigateFrame(host
, true);
1800 // Commit to SiteInstance should be delayed until RenderFrame commits.
1801 EXPECT_EQ(host
, manager
->current_frame_host());
1802 EXPECT_FALSE(host
->GetSiteInstance()->HasSite());
1803 host
->GetSiteInstance()->SetSite(kUrl1
);
1805 // 2) Cross-site navigate to next site. -------------------------
1806 const GURL
kUrl2("http://www.example.com");
1807 NavigationEntryImpl
entry2(
1808 NULL
/* instance */, -1 /* page_id */, kUrl2
, Referrer(),
1809 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED
,
1810 false /* is_renderer_init */);
1811 RenderFrameHostImpl
* host2
= NavigateToEntry(manager
, entry2
);
1813 // A new RenderFrameHost should be created.
1814 ASSERT_EQ(host2
, GetPendingFrameHost(manager
));
1815 EXPECT_NE(host2
, host
);
1817 EXPECT_EQ(host
, manager
->current_frame_host());
1818 EXPECT_FALSE(manager
->current_frame_host()->is_swapped_out());
1819 EXPECT_EQ(host2
, GetPendingFrameHost(manager
));
1821 // 3) Close the tab. -------------------------
1822 notifications
.ListenFor(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED
,
1823 Source
<RenderWidgetHost
>(host2
->render_view_host()));
1824 manager
->OnBeforeUnloadACK(false, true, base::TimeTicks());
1827 notifications
.Check1AndReset(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED
));
1828 EXPECT_FALSE(GetPendingFrameHost(manager
));
1829 EXPECT_EQ(host
, manager
->current_frame_host());
1832 TEST_F(RenderFrameHostManagerTest
, CloseWithPendingWhileUnresponsive
) {
1833 const GURL
kUrl1("http://www.google.com/");
1834 const GURL
kUrl2("http://www.chromium.org/");
1836 CloseWebContentsDelegate close_delegate
;
1837 contents()->SetDelegate(&close_delegate
);
1839 // Navigate to the first page.
1840 contents()->NavigateAndCommit(kUrl1
);
1841 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1843 // Start to close the tab, but assume it's unresponsive.
1844 rfh1
->render_view_host()->ClosePage();
1845 EXPECT_TRUE(rfh1
->render_view_host()->is_waiting_for_close_ack());
1847 // Start a navigation to a new site.
1848 controller().LoadURL(
1849 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1850 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1851 switches::kEnableBrowserSideNavigation
)) {
1852 rfh1
->PrepareForCommit();
1854 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1856 // Simulate the unresponsiveness timer. The tab should close.
1857 contents()->RendererUnresponsive(rfh1
->render_view_host());
1858 EXPECT_TRUE(close_delegate
.is_closed());
1861 // Tests that the RenderFrameHost is properly deleted when the SwapOutACK is
1862 // received. (SwapOut and the corresponding ACK always occur after commit.)
1863 // Also tests that an early SwapOutACK is properly ignored.
1864 TEST_F(RenderFrameHostManagerTest
, DeleteFrameAfterSwapOutACK
) {
1865 const GURL
kUrl1("http://www.google.com/");
1866 const GURL
kUrl2("http://www.chromium.org/");
1868 // Navigate to the first page.
1869 contents()->NavigateAndCommit(kUrl1
);
1870 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1871 RenderFrameHostDeletedObserver
rfh_deleted_observer(rfh1
);
1872 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1874 // Navigate to new site, simulating onbeforeunload approval.
1875 controller().LoadURL(
1876 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1877 int entry_id
= controller().GetPendingEntry()->GetUniqueID();
1878 contents()->GetMainFrame()->PrepareForCommit();
1879 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1880 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1881 TestRenderFrameHost
* rfh2
= contents()->GetPendingMainFrame();
1883 // Simulate the swap out ack, unexpectedly early (before commit). It should
1885 rfh1
->OnSwappedOut();
1886 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1887 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1889 // The new page commits.
1890 contents()->TestDidNavigate(rfh2
, 1, entry_id
, true, kUrl2
,
1891 ui::PAGE_TRANSITION_TYPED
);
1892 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1893 EXPECT_EQ(rfh2
, contents()->GetMainFrame());
1894 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL
);
1895 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
1896 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
1898 rfh1
->frame_tree_node()->render_manager()->IsPendingDeletion(rfh1
));
1900 // Simulate the swap out ack.
1901 rfh1
->OnSwappedOut();
1903 // rfh1 should have been deleted.
1904 EXPECT_TRUE(rfh_deleted_observer
.deleted());
1908 // Tests that the RenderFrameHost is properly swapped out when the SwapOut ACK
1909 // is received. (SwapOut and the corresponding ACK always occur after commit.)
1910 TEST_F(RenderFrameHostManagerTest
, SwapOutFrameAfterSwapOutACK
) {
1911 const GURL
kUrl1("http://www.google.com/");
1912 const GURL
kUrl2("http://www.chromium.org/");
1914 // Navigate to the first page.
1915 contents()->NavigateAndCommit(kUrl1
);
1916 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1917 RenderFrameHostDeletedObserver
rfh_deleted_observer(rfh1
);
1918 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1920 // Increment the number of active frames in SiteInstanceImpl so that rfh1 is
1921 // not deleted on swap out.
1922 rfh1
->GetSiteInstance()->increment_active_frame_count();
1924 // Navigate to new site, simulating onbeforeunload approval.
1925 controller().LoadURL(
1926 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1927 int entry_id
= controller().GetPendingEntry()->GetUniqueID();
1928 contents()->GetMainFrame()->PrepareForCommit();
1929 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1930 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1931 TestRenderFrameHost
* rfh2
= contents()->GetPendingMainFrame();
1933 // The new page commits.
1934 contents()->TestDidNavigate(rfh2
, 1, entry_id
, true, kUrl2
,
1935 ui::PAGE_TRANSITION_TYPED
);
1936 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1937 EXPECT_EQ(rfh2
, contents()->GetMainFrame());
1938 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL
);
1939 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
1940 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
1942 // Simulate the swap out ack.
1943 rfh1
->OnSwappedOut();
1945 // rfh1 should be swapped out or deleted in --site-per-process.
1946 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
1947 switches::kSitePerProcess
)) {
1948 EXPECT_FALSE(rfh_deleted_observer
.deleted());
1949 EXPECT_TRUE(rfh1
->is_swapped_out());
1951 EXPECT_TRUE(rfh_deleted_observer
.deleted());
1955 // Test that the RenderViewHost is properly swapped out if a navigation in the
1956 // new renderer commits before sending the SwapOut message to the old renderer.
1957 // This simulates a cross-site navigation to a synchronously committing URL
1958 // (e.g., a data URL) and ensures it works properly.
1959 TEST_F(RenderFrameHostManagerTest
,
1960 CommitNewNavigationBeforeSendingSwapOut
) {
1961 const GURL
kUrl1("http://www.google.com/");
1962 const GURL
kUrl2("http://www.chromium.org/");
1964 // Navigate to the first page.
1965 contents()->NavigateAndCommit(kUrl1
);
1966 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1967 RenderFrameHostDeletedObserver
rfh_deleted_observer(rfh1
);
1968 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1970 // Increment the number of active frames in SiteInstanceImpl so that rfh1 is
1971 // not deleted on swap out.
1972 scoped_refptr
<SiteInstanceImpl
> site_instance
= rfh1
->GetSiteInstance();
1973 site_instance
->increment_active_frame_count();
1975 // Navigate to new site, simulating onbeforeunload approval.
1976 controller().LoadURL(
1977 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1978 int entry_id
= controller().GetPendingEntry()->GetUniqueID();
1979 rfh1
->PrepareForCommit();
1980 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1981 TestRenderFrameHost
* rfh2
= contents()->GetPendingMainFrame();
1983 // The new page commits.
1984 contents()->TestDidNavigate(rfh2
, 1, entry_id
, true, kUrl2
,
1985 ui::PAGE_TRANSITION_TYPED
);
1986 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1987 EXPECT_EQ(rfh2
, contents()->GetMainFrame());
1988 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL
);
1989 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
1990 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
1992 // Simulate the swap out ack.
1993 rfh1
->OnSwappedOut();
1995 // rfh1 should be swapped out.
1996 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1997 switches::kSitePerProcess
)) {
1998 EXPECT_TRUE(rfh_deleted_observer
.deleted());
1999 EXPECT_TRUE(contents()->GetFrameTree()->root()->render_manager()
2000 ->GetRenderFrameProxyHost(site_instance
.get()));
2002 EXPECT_FALSE(rfh_deleted_observer
.deleted());
2003 EXPECT_TRUE(rfh1
->is_swapped_out());
2007 // Test that a RenderFrameHost is properly deleted when a cross-site navigation
2009 TEST_F(RenderFrameHostManagerTest
,
2010 CancelPendingProperlyDeletesOrSwaps
) {
2011 const GURL
kUrl1("http://www.google.com/");
2012 const GURL
kUrl2("http://www.chromium.org/");
2013 RenderFrameHostImpl
* pending_rfh
= NULL
;
2014 base::TimeTicks now
= base::TimeTicks::Now();
2016 // Navigate to the first page.
2017 contents()->NavigateAndCommit(kUrl1
);
2018 TestRenderFrameHost
* rfh1
= main_test_rfh();
2019 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
2021 // Navigate to a new site, starting a cross-site navigation.
2022 controller().LoadURL(
2023 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
2025 pending_rfh
= contents()->GetFrameTree()->root()->render_manager()
2026 ->pending_frame_host();
2027 RenderFrameHostDeletedObserver
rfh_deleted_observer(pending_rfh
);
2029 // Cancel the navigation by simulating a declined beforeunload dialog.
2030 contents()->GetMainFrame()->OnMessageReceived(
2031 FrameHostMsg_BeforeUnload_ACK(0, false, now
, now
));
2032 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
2034 // Since the pending RFH is the only one for the new SiteInstance, it should
2036 EXPECT_TRUE(rfh_deleted_observer
.deleted());
2039 // Start another cross-site navigation.
2040 controller().LoadURL(
2041 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
2043 pending_rfh
= contents()->GetFrameTree()->root()->render_manager()
2044 ->pending_frame_host();
2045 RenderFrameHostDeletedObserver
rfh_deleted_observer(pending_rfh
);
2047 // Increment the number of active frames in the new SiteInstance, which will
2048 // cause the pending RFH to be deleted and a RenderFrameProxyHost to be
2050 scoped_refptr
<SiteInstanceImpl
> site_instance
=
2051 pending_rfh
->GetSiteInstance();
2052 site_instance
->increment_active_frame_count();
2054 contents()->GetMainFrame()->OnMessageReceived(
2055 FrameHostMsg_BeforeUnload_ACK(0, false, now
, now
));
2056 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
2058 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
2059 switches::kSitePerProcess
)) {
2060 EXPECT_TRUE(rfh_deleted_observer
.deleted());
2061 EXPECT_TRUE(contents()->GetFrameTree()->root()->render_manager()
2062 ->GetRenderFrameProxyHost(site_instance
.get()));
2064 EXPECT_FALSE(rfh_deleted_observer
.deleted());
2069 // Test that a pending RenderFrameHost in a non-root frame tree node is properly
2070 // deleted when the node is detached. Motivated by http://crbug.com/441357 and
2071 // http://crbug.com/444955.
2072 TEST_F(RenderFrameHostManagerTest
, DetachPendingChild
) {
2073 base::CommandLine::ForCurrentProcess()->AppendSwitch(
2074 switches::kSitePerProcess
);
2076 const GURL
kUrlA("http://www.google.com/");
2077 const GURL
kUrlB("http://webkit.org/");
2079 // Create a page with two child frames.
2080 contents()->NavigateAndCommit(kUrlA
);
2081 contents()->GetMainFrame()->OnCreateChildFrame(
2082 contents()->GetMainFrame()->GetProcess()->GetNextRoutingID(),
2083 blink::WebTreeScopeType::Document
, "frame_name",
2084 blink::WebSandboxFlags::None
);
2085 contents()->GetMainFrame()->OnCreateChildFrame(
2086 contents()->GetMainFrame()->GetProcess()->GetNextRoutingID(),
2087 blink::WebTreeScopeType::Document
, "frame_name",
2088 blink::WebSandboxFlags::None
);
2089 RenderFrameHostManager
* root_manager
=
2090 contents()->GetFrameTree()->root()->render_manager();
2091 RenderFrameHostManager
* iframe1
=
2092 contents()->GetFrameTree()->root()->child_at(0)->render_manager();
2093 RenderFrameHostManager
* iframe2
=
2094 contents()->GetFrameTree()->root()->child_at(1)->render_manager();
2096 // 1) The first navigation.
2097 NavigationEntryImpl
entryA(NULL
/* instance */, -1 /* page_id */, kUrlA
,
2098 Referrer(), base::string16() /* title */,
2099 ui::PAGE_TRANSITION_TYPED
,
2100 false /* is_renderer_init */);
2101 RenderFrameHostImpl
* host1
= NavigateToEntry(iframe1
, entryA
);
2103 // The RenderFrameHost created in Init will be reused.
2104 EXPECT_TRUE(host1
== iframe1
->current_frame_host());
2105 EXPECT_FALSE(GetPendingFrameHost(iframe1
));
2108 iframe1
->DidNavigateFrame(host1
, true);
2109 // Commit to SiteInstance should be delayed until RenderFrame commit.
2110 EXPECT_TRUE(host1
== iframe1
->current_frame_host());
2112 EXPECT_TRUE(host1
->GetSiteInstance()->HasSite());
2114 // 2) Cross-site navigate both frames to next site.
2115 NavigationEntryImpl
entryB(NULL
/* instance */, -1 /* page_id */, kUrlB
,
2116 Referrer(kUrlA
, blink::WebReferrerPolicyDefault
),
2117 base::string16() /* title */,
2118 ui::PAGE_TRANSITION_LINK
,
2119 false /* is_renderer_init */);
2120 host1
= NavigateToEntry(iframe1
, entryB
);
2121 RenderFrameHostImpl
* host2
= NavigateToEntry(iframe2
, entryB
);
2123 // A new, pending RenderFrameHost should be created in each FrameTreeNode.
2124 EXPECT_TRUE(GetPendingFrameHost(iframe1
));
2125 EXPECT_TRUE(GetPendingFrameHost(iframe2
));
2126 EXPECT_EQ(host1
, GetPendingFrameHost(iframe1
));
2127 EXPECT_EQ(host2
, GetPendingFrameHost(iframe2
));
2128 EXPECT_TRUE(RenderFrameHostImpl::IsRFHStateActive(
2129 GetPendingFrameHost(iframe1
)->rfh_state()));
2130 EXPECT_TRUE(RenderFrameHostImpl::IsRFHStateActive(
2131 GetPendingFrameHost(iframe2
)->rfh_state()));
2132 EXPECT_NE(GetPendingFrameHost(iframe1
), GetPendingFrameHost(iframe2
));
2133 EXPECT_EQ(GetPendingFrameHost(iframe1
)->GetSiteInstance(),
2134 GetPendingFrameHost(iframe2
)->GetSiteInstance());
2135 EXPECT_NE(iframe1
->current_frame_host(), GetPendingFrameHost(iframe1
));
2136 EXPECT_NE(iframe2
->current_frame_host(), GetPendingFrameHost(iframe2
));
2137 EXPECT_FALSE(contents()->CrossProcessNavigationPending())
2138 << "There should be no top-level pending navigation.";
2140 RenderFrameHostDeletedObserver
delete_watcher1(GetPendingFrameHost(iframe1
));
2141 RenderFrameHostDeletedObserver
delete_watcher2(GetPendingFrameHost(iframe2
));
2142 EXPECT_FALSE(delete_watcher1
.deleted());
2143 EXPECT_FALSE(delete_watcher2
.deleted());
2145 // Keep the SiteInstance alive for testing.
2146 scoped_refptr
<SiteInstanceImpl
> site_instance
=
2147 GetPendingFrameHost(iframe1
)->GetSiteInstance();
2148 EXPECT_TRUE(site_instance
->HasSite());
2149 EXPECT_NE(site_instance
, contents()->GetSiteInstance());
2150 EXPECT_EQ(2U, site_instance
->active_frame_count());
2152 // Proxies should exist.
2154 root_manager
->GetRenderFrameProxyHost(site_instance
.get()));
2156 iframe1
->GetRenderFrameProxyHost(site_instance
.get()));
2158 iframe2
->GetRenderFrameProxyHost(site_instance
.get()));
2160 // Detach the first child FrameTreeNode. This should kill the pending host but
2161 // not yet destroy proxies in |site_instance| since the other child remains.
2162 iframe1
->current_frame_host()->OnMessageReceived(
2163 FrameHostMsg_Detach(iframe1
->current_frame_host()->GetRoutingID()));
2164 iframe1
= NULL
; // Was just destroyed.
2166 EXPECT_TRUE(delete_watcher1
.deleted());
2167 EXPECT_FALSE(delete_watcher2
.deleted());
2168 EXPECT_EQ(1U, site_instance
->active_frame_count());
2170 // Proxies should still exist.
2172 root_manager
->GetRenderFrameProxyHost(site_instance
.get()));
2174 iframe2
->GetRenderFrameProxyHost(site_instance
.get()));
2176 // Detach the second child FrameTreeNode. This should trigger cleanup of
2177 // RenderFrameProxyHosts in |site_instance|.
2178 iframe2
->current_frame_host()->OnMessageReceived(
2179 FrameHostMsg_Detach(iframe2
->current_frame_host()->GetRoutingID()));
2180 iframe2
= NULL
; // Was just destroyed.
2182 EXPECT_TRUE(delete_watcher1
.deleted());
2183 EXPECT_TRUE(delete_watcher2
.deleted());
2185 EXPECT_EQ(0U, site_instance
->active_frame_count());
2187 root_manager
->GetRenderFrameProxyHost(site_instance
.get()))
2188 << "Proxies should have been cleaned up";
2189 EXPECT_TRUE(site_instance
->HasOneRef())
2190 << "This SiteInstance should be destroyable now.";
2193 // Two tabs in the same process crash. The first tab is reloaded, and the second
2194 // tab navigates away without reloading. The second tab's navigation shouldn't
2195 // mess with the first tab's content. Motivated by http://crbug.com/473714.
2196 TEST_F(RenderFrameHostManagerTest
, TwoTabsCrashOneReloadsOneLeaves
) {
2197 base::CommandLine::ForCurrentProcess()->AppendSwitch(
2198 switches::kSitePerProcess
);
2200 const GURL
kUrl1("http://www.google.com/");
2201 const GURL
kUrl2("http://webkit.org/");
2202 const GURL
kUrl3("http://whatwg.org/");
2204 // |contents1| and |contents2| navigate to the same page and then crash.
2205 TestWebContents
* contents1
= contents();
2206 scoped_ptr
<TestWebContents
> contents2(
2207 TestWebContents::Create(browser_context(), contents1
->GetSiteInstance()));
2208 contents1
->NavigateAndCommit(kUrl1
);
2209 contents2
->NavigateAndCommit(kUrl1
);
2210 MockRenderProcessHost
* rph
= contents1
->GetMainFrame()->GetProcess();
2211 EXPECT_EQ(rph
, contents2
->GetMainFrame()->GetProcess());
2212 rph
->SimulateCrash();
2213 EXPECT_FALSE(contents1
->GetMainFrame()->IsRenderFrameLive());
2214 EXPECT_FALSE(contents2
->GetMainFrame()->IsRenderFrameLive());
2215 EXPECT_EQ(contents1
->GetSiteInstance(), contents2
->GetSiteInstance());
2217 // Reload |contents1|.
2218 contents1
->NavigateAndCommit(kUrl1
);
2219 EXPECT_TRUE(contents1
->GetMainFrame()->IsRenderFrameLive());
2220 EXPECT_FALSE(contents2
->GetMainFrame()->IsRenderFrameLive());
2221 EXPECT_EQ(contents1
->GetSiteInstance(), contents2
->GetSiteInstance());
2223 // |contents1| creates an out of process iframe.
2224 contents1
->GetMainFrame()->OnCreateChildFrame(
2225 contents1
->GetMainFrame()->GetProcess()->GetNextRoutingID(),
2226 blink::WebTreeScopeType::Document
, "frame_name",
2227 blink::WebSandboxFlags::None
);
2228 RenderFrameHostManager
* iframe
=
2229 contents()->GetFrameTree()->root()->child_at(0)->render_manager();
2230 NavigationEntryImpl
entry(NULL
/* instance */, -1 /* page_id */, kUrl2
,
2231 Referrer(kUrl1
, blink::WebReferrerPolicyDefault
),
2232 base::string16() /* title */,
2233 ui::PAGE_TRANSITION_LINK
,
2234 false /* is_renderer_init */);
2235 RenderFrameHostImpl
* cross_site
= NavigateToEntry(iframe
, entry
);
2236 iframe
->DidNavigateFrame(cross_site
, true);
2238 // A proxy to the iframe should now exist in the SiteInstance of the main
2240 EXPECT_NE(cross_site
->GetSiteInstance(), contents1
->GetSiteInstance());
2242 iframe
->GetRenderFrameProxyHost(contents1
->GetSiteInstance()));
2244 iframe
->GetRenderFrameProxyHost(contents2
->GetSiteInstance()));
2246 // Navigate |contents2| away from the sad tab (and thus away from the
2247 // SiteInstance of |contents1|). This should not destroy the proxies needed by
2248 // |contents1| -- that was http://crbug.com/473714.
2249 EXPECT_FALSE(contents2
->GetMainFrame()->IsRenderFrameLive());
2250 contents2
->NavigateAndCommit(kUrl3
);
2251 EXPECT_TRUE(contents2
->GetMainFrame()->IsRenderFrameLive());
2253 iframe
->GetRenderFrameProxyHost(contents1
->GetSiteInstance()));
2255 iframe
->GetRenderFrameProxyHost(contents2
->GetSiteInstance()));
2258 } // namespace content