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());
265 int entry_id
= controller().GetPendingEntry()->GetUniqueID();
267 // Simulate the BeforeUnload_ACK that is received from the current renderer
268 // for a cross-site navigation.
269 // PlzNavigate: it is necessary to call PrepareForCommit before getting the
270 // main and the pending frame because when we are trying to navigate to a
271 // WebUI from a new tab, a RenderFrameHost is created to display it that is
272 // committed immediately (since it is a new tab). Therefore the main frame
273 // is replaced without a pending frame being created, and we don't get the
274 // right values for the RFH to navigate: we try to use the old one that has
275 // been deleted in the meantime.
276 contents()->GetMainFrame()->PrepareForCommit();
278 TestRenderFrameHost
* old_rfh
= contents()->GetMainFrame();
279 TestRenderFrameHost
* active_rfh
= contents()->GetPendingMainFrame()
280 ? contents()->GetPendingMainFrame()
282 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, old_rfh
->rfh_state());
284 // Commit the navigation with a new page ID.
285 int32 max_page_id
= contents()->GetMaxPageIDForSiteInstance(
286 active_rfh
->GetSiteInstance());
288 // Use an observer to avoid accessing a deleted renderer later on when the
289 // state is being checked.
290 RenderFrameHostDeletedObserver
rfh_observer(old_rfh
);
291 RenderViewHostDeletedObserver
rvh_observer(old_rfh
->GetRenderViewHost());
292 active_rfh
->SendNavigate(max_page_id
+ 1, entry_id
, true, url
);
294 // Make sure that we start to run the unload handler at the time of commit.
295 bool expecting_rfh_shutdown
= false;
296 if (old_rfh
!= active_rfh
&& !rfh_observer
.deleted()) {
297 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
,
298 old_rfh
->rfh_state());
299 if (!old_rfh
->GetSiteInstance()->active_frame_count()) {
300 expecting_rfh_shutdown
= true;
302 old_rfh
->frame_tree_node()->render_manager()->IsPendingDeletion(
307 // Simulate the swap out ACK coming from the pending renderer. This should
308 // either shut down the old RFH or leave it in a swapped out state.
309 if (old_rfh
!= active_rfh
) {
310 old_rfh
->OnSwappedOut();
311 if (expecting_rfh_shutdown
) {
312 EXPECT_TRUE(rfh_observer
.deleted());
313 EXPECT_TRUE(rvh_observer
.deleted());
315 EXPECT_EQ(RenderFrameHostImpl::STATE_SWAPPED_OUT
,
316 old_rfh
->rfh_state());
319 EXPECT_EQ(active_rfh
, contents()->GetMainFrame());
320 EXPECT_EQ(NULL
, contents()->GetPendingMainFrame());
323 bool ShouldSwapProcesses(RenderFrameHostManager
* manager
,
324 const NavigationEntryImpl
* current_entry
,
325 const NavigationEntryImpl
* new_entry
) const {
327 BrowserContext
* browser_context
=
328 manager
->delegate_
->GetControllerForRenderManager().GetBrowserContext();
329 const GURL
& current_effective_url
= current_entry
?
330 SiteInstanceImpl::GetEffectiveURL(browser_context
,
331 current_entry
->GetURL()) :
332 manager
->render_frame_host_
->GetSiteInstance()->GetSiteURL();
333 bool current_is_view_source_mode
= current_entry
?
334 current_entry
->IsViewSourceMode() : new_entry
->IsViewSourceMode();
335 return manager
->ShouldSwapBrowsingInstancesForNavigation(
336 current_effective_url
,
337 current_is_view_source_mode
,
338 new_entry
->site_instance(),
339 SiteInstanceImpl::GetEffectiveURL(browser_context
, new_entry
->GetURL()),
340 new_entry
->IsViewSourceMode());
343 // Creates a test RenderFrameHost that's swapped out.
344 TestRenderFrameHost
* CreateSwappedOutRenderFrameHost() {
345 const GURL
kChromeURL("chrome://foo");
346 const GURL
kDestUrl("http://www.google.com/");
348 // Navigate our first tab to a chrome url and then to the destination.
349 NavigateActiveAndCommit(kChromeURL
);
350 TestRenderFrameHost
* ntp_rfh
= contents()->GetMainFrame();
352 // Navigate to a cross-site URL.
353 contents()->GetController().LoadURL(
354 kDestUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
355 int entry_id
= contents()->GetController().GetPendingEntry()->GetUniqueID();
356 contents()->GetMainFrame()->PrepareForCommit();
357 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
359 // Manually increase the number of active frames in the
360 // SiteInstance that ntp_rfh belongs to, to prevent it from being
361 // destroyed when it gets swapped out.
362 ntp_rfh
->GetSiteInstance()->increment_active_frame_count();
364 TestRenderFrameHost
* dest_rfh
= contents()->GetPendingMainFrame();
366 EXPECT_NE(ntp_rfh
, dest_rfh
);
368 // BeforeUnload finishes.
369 ntp_rfh
->SendBeforeUnloadACK(true);
371 dest_rfh
->SendNavigate(101, entry_id
, true, kDestUrl
);
372 ntp_rfh
->OnSwappedOut();
374 EXPECT_TRUE(ntp_rfh
->is_swapped_out());
378 // Returns the RenderFrameHost that should be used in the navigation to
380 RenderFrameHostImpl
* NavigateToEntry(
381 RenderFrameHostManager
* manager
,
382 const NavigationEntryImpl
& entry
) {
383 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
384 switches::kEnableBrowserSideNavigation
)) {
385 scoped_ptr
<NavigationRequest
> navigation_request
=
386 NavigationRequest::CreateBrowserInitiated(
387 manager
->frame_tree_node_
, entry
, FrameMsg_Navigate_Type::NORMAL
,
388 base::TimeTicks::Now(),
389 static_cast<NavigationControllerImpl
*>(&controller()));
390 TestRenderFrameHost
* frame_host
= static_cast<TestRenderFrameHost
*>(
391 manager
->GetFrameHostForNavigation(*navigation_request
));
393 frame_host
->set_pending_commit(true);
396 return manager
->Navigate(entry
);
399 // Returns the pending RenderFrameHost.
400 // PlzNavigate: returns the speculative RenderFrameHost.
401 RenderFrameHostImpl
* GetPendingFrameHost(
402 RenderFrameHostManager
* manager
) {
403 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
404 switches::kEnableBrowserSideNavigation
)) {
405 return manager
->speculative_render_frame_host_
.get();
407 return manager
->pending_frame_host();
411 RenderFrameHostManagerTestWebUIControllerFactory factory_
;
414 // Tests that when you navigate from a chrome:// url to another page, and
415 // then do that same thing in another tab, that the two resulting pages have
416 // different SiteInstances, BrowsingInstances, and RenderProcessHosts. This is
417 // a regression test for bug 9364.
418 TEST_F(RenderFrameHostManagerTest
, NewTabPageProcesses
) {
419 set_should_create_webui(true);
420 const GURL
kChromeUrl("chrome://foo");
421 const GURL
kDestUrl("http://www.google.com/");
423 // Navigate our first tab to the chrome url and then to the destination,
424 // ensuring we grant bindings to the chrome URL.
425 NavigateActiveAndCommit(kChromeUrl
);
426 EXPECT_TRUE(active_rvh()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
427 NavigateActiveAndCommit(kDestUrl
);
429 EXPECT_FALSE(contents()->GetPendingMainFrame());
431 // Make a second tab.
432 scoped_ptr
<TestWebContents
> contents2(
433 TestWebContents::Create(browser_context(), NULL
));
435 // Load the two URLs in the second tab. Note that the first navigation creates
436 // a RFH that's not pending (since there is no cross-site transition), so
437 // we use the committed one.
438 contents2
->GetController().LoadURL(
439 kChromeUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
440 int entry_id
= contents2
->GetController().GetPendingEntry()->GetUniqueID();
441 contents2
->GetMainFrame()->PrepareForCommit();
442 TestRenderFrameHost
* ntp_rfh2
= contents2
->GetMainFrame();
443 EXPECT_FALSE(contents2
->CrossProcessNavigationPending());
444 ntp_rfh2
->SendNavigate(100, entry_id
, true, kChromeUrl
);
446 // The second one is the opposite, creating a cross-site transition and
447 // requiring a beforeunload ack.
448 contents2
->GetController().LoadURL(
449 kDestUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
450 entry_id
= contents2
->GetController().GetPendingEntry()->GetUniqueID();
451 contents2
->GetMainFrame()->PrepareForCommit();
452 EXPECT_TRUE(contents2
->CrossProcessNavigationPending());
453 TestRenderFrameHost
* dest_rfh2
= contents2
->GetPendingMainFrame();
454 ASSERT_TRUE(dest_rfh2
);
456 dest_rfh2
->SendNavigate(101, entry_id
, true, kDestUrl
);
458 // The two RFH's should be different in every way.
459 EXPECT_NE(contents()->GetMainFrame()->GetProcess(), dest_rfh2
->GetProcess());
460 EXPECT_NE(contents()->GetMainFrame()->GetSiteInstance(),
461 dest_rfh2
->GetSiteInstance());
462 EXPECT_FALSE(dest_rfh2
->GetSiteInstance()->IsRelatedSiteInstance(
463 contents()->GetMainFrame()->GetSiteInstance()));
465 // Navigate both to the new tab page, and verify that they share a
466 // RenderProcessHost (not a SiteInstance).
467 NavigateActiveAndCommit(kChromeUrl
);
468 EXPECT_FALSE(contents()->GetPendingMainFrame());
470 contents2
->GetController().LoadURL(
471 kChromeUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
472 entry_id
= contents2
->GetController().GetPendingEntry()->GetUniqueID();
473 contents2
->GetMainFrame()->PrepareForCommit();
474 contents2
->GetPendingMainFrame()->SendNavigate(102, entry_id
, true,
477 EXPECT_NE(contents()->GetMainFrame()->GetSiteInstance(),
478 contents2
->GetMainFrame()->GetSiteInstance());
479 EXPECT_EQ(contents()->GetMainFrame()->GetSiteInstance()->GetProcess(),
480 contents2
->GetMainFrame()->GetSiteInstance()->GetProcess());
483 // Ensure that the browser ignores most IPC messages that arrive from a
484 // RenderViewHost that has been swapped out. We do not want to take
485 // action on requests from a non-active renderer. The main exception is
486 // for synchronous messages, which cannot be ignored without leaving the
487 // renderer in a stuck state. See http://crbug.com/93427.
488 TEST_F(RenderFrameHostManagerTest
, FilterMessagesWhileSwappedOut
) {
489 const GURL
kChromeURL("chrome://foo");
490 const GURL
kDestUrl("http://www.google.com/");
491 std::vector
<FaviconURL
> icons
;
493 // Navigate our first tab to a chrome url and then to the destination.
494 NavigateActiveAndCommit(kChromeURL
);
495 TestRenderFrameHost
* ntp_rfh
= contents()->GetMainFrame();
497 // Send an update favicon message and make sure it works.
499 PluginFaviconMessageObserver
observer(contents());
500 EXPECT_TRUE(ntp_rfh
->GetRenderViewHost()->OnMessageReceived(
501 ViewHostMsg_UpdateFaviconURL(
502 ntp_rfh
->GetRenderViewHost()->GetRoutingID(), icons
)));
503 EXPECT_TRUE(observer
.favicon_received());
505 // Create one more frame in the same SiteInstance where ntp_rfh
506 // exists so that it doesn't get deleted on navigation to another
508 ntp_rfh
->GetSiteInstance()->increment_active_frame_count();
510 // Navigate to a cross-site URL.
511 NavigateActiveAndCommit(kDestUrl
);
512 TestRenderFrameHost
* dest_rfh
= contents()->GetMainFrame();
513 ASSERT_TRUE(dest_rfh
);
514 EXPECT_NE(ntp_rfh
, dest_rfh
);
516 // The new RVH should be able to update its favicon.
518 PluginFaviconMessageObserver
observer(contents());
520 dest_rfh
->GetRenderViewHost()->OnMessageReceived(
521 ViewHostMsg_UpdateFaviconURL(
522 dest_rfh
->GetRenderViewHost()->GetRoutingID(), icons
)));
523 EXPECT_TRUE(observer
.favicon_received());
526 // The old renderer, being slow, now updates the favicon. It should be
527 // filtered out and not take effect.
528 EXPECT_TRUE(ntp_rfh
->is_swapped_out());
530 PluginFaviconMessageObserver
observer(contents());
532 ntp_rfh
->GetRenderViewHost()->OnMessageReceived(
533 ViewHostMsg_UpdateFaviconURL(
534 dest_rfh
->GetRenderViewHost()->GetRoutingID(), icons
)));
535 EXPECT_FALSE(observer
.favicon_received());
538 #if defined(ENABLE_PLUGINS)
539 // The same logic should apply to RenderFrameHosts as well and routing through
540 // swapped out RFH shouldn't be allowed. Use a PluginCrashObserver to check
541 // if the IPC message is allowed through or not.
543 PluginFaviconMessageObserver
observer(contents());
544 EXPECT_TRUE(ntp_rfh
->OnMessageReceived(
545 FrameHostMsg_PluginCrashed(
546 ntp_rfh
->GetRoutingID(), base::FilePath(), 0)));
547 EXPECT_FALSE(observer
.plugin_crashed());
551 // We cannot filter out synchronous IPC messages, because the renderer would
552 // be left waiting for a reply. We pick RunBeforeUnloadConfirm as an example
553 // that can run easily within a unit test, and that needs to receive a reply
554 // without showing an actual dialog.
555 MockRenderProcessHost
* ntp_process_host
= ntp_rfh
->GetProcess();
556 ntp_process_host
->sink().ClearMessages();
557 const base::string16 msg
= base::ASCIIToUTF16("Message");
559 base::string16 unused
;
560 FrameHostMsg_RunBeforeUnloadConfirm
before_unload_msg(
561 ntp_rfh
->GetRoutingID(), kChromeURL
, msg
, false, &result
, &unused
);
562 // Enable pumping for check in BrowserMessageFilter::CheckCanDispatchOnUI.
563 before_unload_msg
.EnableMessagePumping();
564 EXPECT_TRUE(ntp_rfh
->OnMessageReceived(before_unload_msg
));
565 EXPECT_TRUE(ntp_process_host
->sink().GetUniqueMessageMatching(IPC_REPLY_ID
));
567 // Also test RunJavaScriptMessage.
568 ntp_process_host
->sink().ClearMessages();
569 FrameHostMsg_RunJavaScriptMessage
js_msg(
570 ntp_rfh
->GetRoutingID(), msg
, msg
, kChromeURL
,
571 JAVASCRIPT_MESSAGE_TYPE_CONFIRM
, &result
, &unused
);
572 js_msg
.EnableMessagePumping();
573 EXPECT_TRUE(ntp_rfh
->OnMessageReceived(js_msg
));
574 EXPECT_TRUE(ntp_process_host
->sink().GetUniqueMessageMatching(IPC_REPLY_ID
));
577 // Test that the ViewHostMsg_UpdateFaviconURL IPC message is ignored if the
578 // renderer is in the STATE_PENDING_SWAP_OUT_STATE. The favicon code assumes
579 // that it only gets ViewHostMsg_UpdateFaviconURL messages for the most recently
580 // committed navigation for each WebContentsImpl.
581 TEST_F(RenderFrameHostManagerTest
, UpdateFaviconURLWhilePendingSwapOut
) {
582 const GURL
kChromeURL("chrome://foo");
583 const GURL
kDestUrl("http://www.google.com/");
584 std::vector
<FaviconURL
> icons
;
586 // Navigate our first tab to a chrome url and then to the destination.
587 NavigateActiveAndCommit(kChromeURL
);
588 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
590 // Send an update favicon message and make sure it works.
592 PluginFaviconMessageObserver
observer(contents());
593 EXPECT_TRUE(rfh1
->GetRenderViewHost()->OnMessageReceived(
594 ViewHostMsg_UpdateFaviconURL(
595 rfh1
->GetRenderViewHost()->GetRoutingID(), icons
)));
596 EXPECT_TRUE(observer
.favicon_received());
599 // Create one more frame in the same SiteInstance where |rfh1| exists so that
600 // it doesn't get deleted on navigation to another site.
601 rfh1
->GetSiteInstance()->increment_active_frame_count();
603 // Navigate to a cross-site URL and commit the new page.
604 controller().LoadURL(
605 kDestUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
606 int entry_id
= controller().GetPendingEntry()->GetUniqueID();
607 contents()->GetMainFrame()->PrepareForCommit();
608 TestRenderFrameHost
* rfh2
= contents()->GetPendingMainFrame();
609 contents()->TestDidNavigate(rfh2
, 1, entry_id
, true, kDestUrl
,
610 ui::PAGE_TRANSITION_TYPED
);
611 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
612 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
614 // The new RVH should be able to update its favicons.
616 PluginFaviconMessageObserver
observer(contents());
617 EXPECT_TRUE(rfh2
->GetRenderViewHost()->OnMessageReceived(
618 ViewHostMsg_UpdateFaviconURL(rfh2
->GetRenderViewHost()->GetRoutingID(),
620 EXPECT_TRUE(observer
.favicon_received());
623 // The old renderer, being slow, now updates its favicons. The message should
626 PluginFaviconMessageObserver
observer(contents());
627 EXPECT_TRUE(rfh1
->GetRenderViewHost()->OnMessageReceived(
628 ViewHostMsg_UpdateFaviconURL(rfh1
->GetRenderViewHost()->GetRoutingID(),
630 EXPECT_FALSE(observer
.favicon_received());
634 // Ensure that frames aren't added to the frame tree, if the message is coming
635 // from a process different than the parent frame's current RenderFrameHost
636 // process. Otherwise it is possible to have collisions of routing ids, as they
637 // are scoped per process. See https://crbug.com/415059.
638 TEST_F(RenderFrameHostManagerTest
, DropCreateChildFrameWhileSwappedOut
) {
639 const GURL
kUrl1("http://foo.com");
640 const GURL
kUrl2("http://www.google.com/");
642 // Navigate to the first site.
643 NavigateActiveAndCommit(kUrl1
);
644 TestRenderFrameHost
* initial_rfh
= contents()->GetMainFrame();
646 RenderFrameHostCreatedObserver
observer(contents());
647 initial_rfh
->OnCreateChildFrame(
648 initial_rfh
->GetProcess()->GetNextRoutingID(), std::string(),
650 EXPECT_TRUE(observer
.created());
653 // Create one more frame in the same SiteInstance where initial_rfh
654 // exists so that initial_rfh doesn't get deleted on navigation to another
656 initial_rfh
->GetSiteInstance()->increment_active_frame_count();
658 // Navigate to a cross-site URL.
659 NavigateActiveAndCommit(kUrl2
);
660 EXPECT_TRUE(initial_rfh
->is_swapped_out());
662 TestRenderFrameHost
* dest_rfh
= contents()->GetMainFrame();
663 ASSERT_TRUE(dest_rfh
);
664 EXPECT_NE(initial_rfh
, dest_rfh
);
667 // Since the old RFH is now swapped out, it shouldn't process any messages
668 // to create child frames.
669 RenderFrameHostCreatedObserver
observer(contents());
670 initial_rfh
->OnCreateChildFrame(
671 initial_rfh
->GetProcess()->GetNextRoutingID(), std::string(),
673 EXPECT_FALSE(observer
.created());
677 TEST_F(RenderFrameHostManagerTest
, WhiteListSwapCompositorFrame
) {
678 TestRenderFrameHost
* swapped_out_rfh
= CreateSwappedOutRenderFrameHost();
679 TestRenderWidgetHostView
* swapped_out_rwhv
=
680 static_cast<TestRenderWidgetHostView
*>(
681 swapped_out_rfh
->GetRenderViewHost()->GetView());
682 EXPECT_FALSE(swapped_out_rwhv
->did_swap_compositor_frame());
684 MockRenderProcessHost
* process_host
= swapped_out_rfh
->GetProcess();
685 process_host
->sink().ClearMessages();
687 cc::CompositorFrame frame
;
688 ViewHostMsg_SwapCompositorFrame
msg(
689 rvh()->GetRoutingID(), 0, frame
, std::vector
<IPC::Message
>());
691 EXPECT_TRUE(swapped_out_rfh
->render_view_host()->OnMessageReceived(msg
));
692 EXPECT_TRUE(swapped_out_rwhv
->did_swap_compositor_frame());
695 // Test if RenderViewHost::GetRenderWidgetHosts() only returns active
697 TEST_F(RenderFrameHostManagerTest
, GetRenderWidgetHostsReturnsActiveViews
) {
698 TestRenderFrameHost
* swapped_out_rfh
= CreateSwappedOutRenderFrameHost();
699 EXPECT_TRUE(swapped_out_rfh
->is_swapped_out());
701 scoped_ptr
<RenderWidgetHostIterator
> widgets(
702 RenderWidgetHost::GetRenderWidgetHosts());
703 // We know that there is the only one active widget. Another view is
704 // now swapped out, so the swapped out view is not included in the
706 RenderWidgetHost
* widget
= widgets
->GetNextHost();
707 EXPECT_FALSE(widgets
->GetNextHost());
708 RenderViewHost
* rvh
= RenderViewHost::From(widget
);
709 EXPECT_TRUE(static_cast<RenderViewHostImpl
*>(rvh
)->is_active());
712 // Test if RenderViewHost::GetRenderWidgetHosts() returns a subset of
713 // RenderViewHostImpl::GetAllRenderWidgetHosts().
714 // RenderViewHost::GetRenderWidgetHosts() returns only active widgets, but
715 // RenderViewHostImpl::GetAllRenderWidgetHosts() returns everything
716 // including swapped out ones.
717 TEST_F(RenderFrameHostManagerTest
,
718 GetRenderWidgetHostsWithinGetAllRenderWidgetHosts
) {
719 TestRenderFrameHost
* swapped_out_rfh
= CreateSwappedOutRenderFrameHost();
720 EXPECT_TRUE(swapped_out_rfh
->is_swapped_out());
722 scoped_ptr
<RenderWidgetHostIterator
> widgets(
723 RenderWidgetHost::GetRenderWidgetHosts());
725 while (RenderWidgetHost
* w
= widgets
->GetNextHost()) {
727 scoped_ptr
<RenderWidgetHostIterator
> all_widgets(
728 RenderWidgetHostImpl::GetAllRenderWidgetHosts());
729 while (RenderWidgetHost
* widget
= all_widgets
->GetNextHost()) {
739 // Test if SiteInstanceImpl::active_frame_count() is correctly updated
740 // as frames in a SiteInstance get swapped out and in.
741 TEST_F(RenderFrameHostManagerTest
, ActiveFrameCountWhileSwappingInAndOut
) {
742 const GURL
kUrl1("http://www.google.com/");
743 const GURL
kUrl2("http://www.chromium.org/");
745 // Navigate to an initial URL.
746 contents()->NavigateAndCommit(kUrl1
);
747 TestRenderFrameHost
* rfh1
= main_test_rfh();
749 SiteInstanceImpl
* instance1
= rfh1
->GetSiteInstance();
750 EXPECT_EQ(instance1
->active_frame_count(), 1U);
752 // Create 2 new tabs and simulate them being the opener chain for the main
753 // tab. They should be in the same SiteInstance.
754 scoped_ptr
<TestWebContents
> opener1(
755 TestWebContents::Create(browser_context(), instance1
));
756 contents()->SetOpener(opener1
.get());
758 scoped_ptr
<TestWebContents
> opener2(
759 TestWebContents::Create(browser_context(), instance1
));
760 opener1
->SetOpener(opener2
.get());
762 EXPECT_EQ(instance1
->active_frame_count(), 3U);
764 // Navigate to a cross-site URL (different SiteInstance but same
765 // BrowsingInstance).
766 contents()->NavigateAndCommit(kUrl2
);
767 TestRenderFrameHost
* rfh2
= main_test_rfh();
768 SiteInstanceImpl
* instance2
= rfh2
->GetSiteInstance();
770 // rvh2 is on chromium.org which is different from google.com on
771 // which other tabs are.
772 EXPECT_EQ(instance2
->active_frame_count(), 1U);
774 // There are two active views on google.com now.
775 EXPECT_EQ(instance1
->active_frame_count(), 2U);
777 // Navigate to the original origin (google.com).
778 contents()->NavigateAndCommit(kUrl1
);
780 EXPECT_EQ(instance1
->active_frame_count(), 3U);
783 // This deletes a WebContents when the given RVH is deleted. This is
784 // only for testing whether deleting an RVH does not cause any UaF in
785 // other parts of the system. For now, this class is only used for the
786 // next test cases to detect the bug mentioned at
787 // http://crbug.com/259859.
788 class RenderViewHostDestroyer
: public WebContentsObserver
{
790 RenderViewHostDestroyer(RenderViewHost
* render_view_host
,
791 WebContents
* web_contents
)
792 : WebContentsObserver(WebContents::FromRenderViewHost(render_view_host
)),
793 render_view_host_(render_view_host
),
794 web_contents_(web_contents
) {}
796 void RenderViewDeleted(RenderViewHost
* render_view_host
) override
{
797 if (render_view_host
== render_view_host_
)
798 delete web_contents_
;
802 RenderViewHost
* render_view_host_
;
803 WebContents
* web_contents_
;
805 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestroyer
);
808 // Test if ShutdownRenderViewHostsInSiteInstance() does not touch any
809 // RenderWidget that has been freed while deleting a RenderViewHost in
810 // a previous iteration. This is a regression test for
811 // http://crbug.com/259859.
812 TEST_F(RenderFrameHostManagerTest
,
813 DetectUseAfterFreeInShutdownRenderViewHostsInSiteInstance
) {
814 const GURL
kChromeURL("chrome://newtab");
815 const GURL
kUrl1("http://www.google.com");
816 const GURL
kUrl2("http://www.chromium.org");
818 // Navigate our first tab to a chrome url and then to the destination.
819 NavigateActiveAndCommit(kChromeURL
);
820 TestRenderFrameHost
* ntp_rfh
= contents()->GetMainFrame();
822 // Create one more tab and navigate to kUrl1. web_contents is not
823 // wrapped as scoped_ptr since it intentionally deleted by destroyer
824 // below as part of this test.
825 TestWebContents
* web_contents
=
826 TestWebContents::Create(browser_context(), ntp_rfh
->GetSiteInstance());
827 web_contents
->NavigateAndCommit(kUrl1
);
828 RenderViewHostDestroyer
destroyer(ntp_rfh
->GetRenderViewHost(),
831 // This causes the first tab to navigate to kUrl2, which destroys
832 // the ntp_rfh in ShutdownRenderViewHostsInSiteInstance(). When
833 // ntp_rfh is destroyed, it also destroys the RVHs in web_contents
834 // too. This can test whether
835 // SiteInstanceImpl::ShutdownRenderViewHostsInSiteInstance() can
836 // touch any object freed in this way or not while iterating through
838 contents()->NavigateAndCommit(kUrl2
);
841 // When there is an error with the specified page, renderer exits view-source
842 // mode. See WebFrameImpl::DidFail(). We check by this test that
843 // EnableViewSourceMode message is sent on every navigation regardless
844 // RenderView is being newly created or reused.
845 TEST_F(RenderFrameHostManagerTest
, AlwaysSendEnableViewSourceMode
) {
846 const GURL
kChromeUrl("chrome://foo/");
847 const GURL
kUrl("http://foo/");
848 const GURL
kViewSourceUrl("view-source:http://foo/");
850 // We have to navigate to some page at first since without this, the first
851 // navigation will reuse the SiteInstance created by Init(), and the second
852 // one will create a new SiteInstance. Because current_instance and
853 // new_instance will be different, a new RenderViewHost will be created for
854 // the second navigation. We have to avoid this in order to exercise the
856 NavigateActiveAndCommit(kChromeUrl
);
858 // Navigate. Note that "view source" URLs are implemented by putting the RFH
859 // into a view-source mode and then navigating to the inner URL, so that's why
860 // the bare URL is what's committed and returned by the last committed entry's
862 controller().LoadURL(
863 kViewSourceUrl
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
864 int entry_id
= controller().GetPendingEntry()->GetUniqueID();
866 // Simulate response from RenderFrame for DispatchBeforeUnload.
867 contents()->GetMainFrame()->PrepareForCommit();
868 ASSERT_TRUE(contents()->GetPendingMainFrame())
869 << "Expected new pending RenderFrameHost to be created.";
870 RenderFrameHost
* last_rfh
= contents()->GetPendingMainFrame();
872 contents()->GetMaxPageIDForSiteInstance(last_rfh
->GetSiteInstance()) + 1;
873 contents()->GetPendingMainFrame()->SendNavigate(new_id
, entry_id
, true, kUrl
);
875 EXPECT_EQ(1, controller().GetLastCommittedEntryIndex());
876 NavigationEntry
* last_committed
= controller().GetLastCommittedEntry();
877 ASSERT_NE(nullptr, last_committed
);
878 EXPECT_EQ(kUrl
, last_committed
->GetURL());
879 EXPECT_EQ(kViewSourceUrl
, last_committed
->GetVirtualURL());
880 EXPECT_FALSE(controller().GetPendingEntry());
881 // Because we're using TestWebContents and TestRenderViewHost in this
882 // unittest, no one calls WebContentsImpl::RenderViewCreated(). So, we see no
883 // EnableViewSourceMode message, here.
885 // Clear queued messages before load.
886 process()->sink().ClearMessages();
889 controller().LoadURL(
890 kViewSourceUrl
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
891 entry_id
= controller().GetPendingEntry()->GetUniqueID();
892 contents()->GetMainFrame()->PrepareForCommit();
894 // The same RenderViewHost should be reused.
895 EXPECT_FALSE(contents()->GetPendingMainFrame());
896 EXPECT_EQ(last_rfh
, contents()->GetMainFrame());
898 // The renderer sends a commit.
899 contents()->GetMainFrame()->SendNavigateWithTransition(
900 new_id
, entry_id
, false, kUrl
, ui::PAGE_TRANSITION_TYPED
);
901 EXPECT_EQ(1, controller().GetLastCommittedEntryIndex());
902 EXPECT_FALSE(controller().GetPendingEntry());
904 // New message should be sent out to make sure to enter view-source mode.
905 EXPECT_TRUE(process()->sink().GetUniqueMessageMatching(
906 ViewMsg_EnableViewSourceMode::ID
));
909 // Tests the Init function by checking the initial RenderViewHost.
910 TEST_F(RenderFrameHostManagerTest
, Init
) {
911 // Using TestBrowserContext.
912 SiteInstanceImpl
* instance
=
913 static_cast<SiteInstanceImpl
*>(SiteInstance::Create(browser_context()));
914 EXPECT_FALSE(instance
->HasSite());
916 scoped_ptr
<TestWebContents
> web_contents(
917 TestWebContents::Create(browser_context(), instance
));
919 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
920 RenderViewHostImpl
* rvh
= manager
->current_host();
921 RenderFrameHostImpl
* rfh
= manager
->current_frame_host();
924 EXPECT_EQ(rvh
, rfh
->render_view_host());
925 EXPECT_EQ(instance
, rvh
->GetSiteInstance());
926 EXPECT_EQ(web_contents
.get(), rvh
->GetDelegate());
927 EXPECT_EQ(web_contents
.get(), rfh
->delegate());
928 EXPECT_TRUE(manager
->GetRenderWidgetHostView());
929 EXPECT_FALSE(manager
->pending_render_view_host());
932 // Tests the Navigate function. We navigate three sites consecutively and check
933 // how the pending/committed RenderViewHost are modified.
934 TEST_F(RenderFrameHostManagerTest
, Navigate
) {
935 TestNotificationTracker notifications
;
937 SiteInstance
* instance
= SiteInstance::Create(browser_context());
939 scoped_ptr
<TestWebContents
> web_contents(
940 TestWebContents::Create(browser_context(), instance
));
941 notifications
.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED
,
942 Source
<WebContents
>(web_contents
.get()));
944 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
945 RenderFrameHostImpl
* host
= NULL
;
947 // 1) The first navigation. --------------------------
948 const GURL
kUrl1("http://www.google.com/");
949 NavigationEntryImpl
entry1(
950 NULL
/* instance */, -1 /* page_id */, kUrl1
, Referrer(),
951 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED
,
952 false /* is_renderer_init */);
953 host
= NavigateToEntry(manager
, entry1
);
955 // The RenderFrameHost created in Init will be reused.
956 EXPECT_TRUE(host
== manager
->current_frame_host());
957 EXPECT_FALSE(GetPendingFrameHost(manager
));
960 manager
->DidNavigateFrame(host
, true);
961 // Commit to SiteInstance should be delayed until RenderFrame commit.
962 EXPECT_TRUE(host
== manager
->current_frame_host());
964 EXPECT_FALSE(host
->GetSiteInstance()->HasSite());
965 host
->GetSiteInstance()->SetSite(kUrl1
);
967 // 2) Navigate to next site. -------------------------
968 const GURL
kUrl2("http://www.google.com/foo");
969 NavigationEntryImpl
entry2(
970 NULL
/* instance */, -1 /* page_id */, kUrl2
,
971 Referrer(kUrl1
, blink::WebReferrerPolicyDefault
),
972 base::string16() /* title */, ui::PAGE_TRANSITION_LINK
,
973 true /* is_renderer_init */);
974 host
= NavigateToEntry(manager
, entry2
);
976 // The RenderFrameHost created in Init will be reused.
977 EXPECT_TRUE(host
== manager
->current_frame_host());
978 EXPECT_FALSE(GetPendingFrameHost(manager
));
981 manager
->DidNavigateFrame(host
, true);
982 EXPECT_TRUE(host
== manager
->current_frame_host());
984 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
986 // 3) Cross-site navigate to next site. --------------
987 const GURL
kUrl3("http://webkit.org/");
988 NavigationEntryImpl
entry3(
989 NULL
/* instance */, -1 /* page_id */, kUrl3
,
990 Referrer(kUrl2
, blink::WebReferrerPolicyDefault
),
991 base::string16() /* title */, ui::PAGE_TRANSITION_LINK
,
992 false /* is_renderer_init */);
993 host
= NavigateToEntry(manager
, entry3
);
995 // A new RenderFrameHost should be created.
996 EXPECT_TRUE(GetPendingFrameHost(manager
));
997 ASSERT_EQ(host
, GetPendingFrameHost(manager
));
999 notifications
.Reset();
1002 manager
->DidNavigateFrame(GetPendingFrameHost(manager
), true);
1003 EXPECT_TRUE(host
== manager
->current_frame_host());
1005 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
1006 // Check the pending RenderFrameHost has been committed.
1007 EXPECT_FALSE(GetPendingFrameHost(manager
));
1009 // We should observe a notification.
1011 notifications
.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED
));
1014 // Tests WebUI creation.
1015 TEST_F(RenderFrameHostManagerTest
, WebUI
) {
1016 set_should_create_webui(true);
1017 SiteInstance
* instance
= SiteInstance::Create(browser_context());
1019 scoped_ptr
<TestWebContents
> web_contents(
1020 TestWebContents::Create(browser_context(), instance
));
1021 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
1022 RenderFrameHostImpl
* initial_rfh
= manager
->current_frame_host();
1024 EXPECT_FALSE(manager
->current_host()->IsRenderViewLive());
1025 EXPECT_FALSE(manager
->web_ui());
1026 EXPECT_TRUE(initial_rfh
);
1028 const GURL
kUrl("chrome://foo");
1029 NavigationEntryImpl
entry(NULL
/* instance */, -1 /* page_id */, kUrl
,
1030 Referrer(), base::string16() /* title */,
1031 ui::PAGE_TRANSITION_TYPED
,
1032 false /* is_renderer_init */);
1033 RenderFrameHostImpl
* host
= NavigateToEntry(manager
, entry
);
1035 // We commit the pending RenderFrameHost immediately because the previous
1036 // RenderFrameHost was not live. We test a case where it is live in
1039 EXPECT_NE(initial_rfh
, host
);
1040 EXPECT_EQ(host
, manager
->current_frame_host());
1041 EXPECT_FALSE(GetPendingFrameHost(manager
));
1043 // It's important that the SiteInstance get set on the Web UI page as soon
1044 // as the navigation starts, rather than lazily after it commits, so we don't
1045 // try to re-use the SiteInstance/process for non Web UI things that may
1046 // get loaded in between.
1047 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
1048 EXPECT_EQ(kUrl
, host
->GetSiteInstance()->GetSiteURL());
1050 // The Web UI is committed immediately because the RenderViewHost has not been
1051 // used yet. UpdateStateForNavigate() took the short cut path.
1052 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1053 switches::kEnableBrowserSideNavigation
)) {
1054 EXPECT_FALSE(manager
->speculative_web_ui());
1056 EXPECT_FALSE(manager
->pending_web_ui());
1058 EXPECT_TRUE(manager
->web_ui());
1061 manager
->DidNavigateFrame(host
, true);
1063 host
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1066 // Tests that we can open a WebUI link in a new tab from a WebUI page and still
1067 // grant the correct bindings. http://crbug.com/189101.
1068 TEST_F(RenderFrameHostManagerTest
, WebUIInNewTab
) {
1069 set_should_create_webui(true);
1070 SiteInstance
* blank_instance
= SiteInstance::Create(browser_context());
1072 // Create a blank tab.
1073 scoped_ptr
<TestWebContents
> web_contents1(
1074 TestWebContents::Create(browser_context(), blank_instance
));
1075 RenderFrameHostManager
* manager1
=
1076 web_contents1
->GetRenderManagerForTesting();
1077 // Test the case that new RVH is considered live.
1078 manager1
->current_host()->CreateRenderView(
1079 base::string16(), -1, MSG_ROUTING_NONE
, -1, false);
1080 EXPECT_TRUE(manager1
->current_host()->IsRenderViewLive());
1081 EXPECT_TRUE(manager1
->current_frame_host()->IsRenderFrameLive());
1083 // Navigate to a WebUI page.
1084 const GURL
kUrl1("chrome://foo");
1085 NavigationEntryImpl
entry1(NULL
/* instance */, -1 /* page_id */, kUrl1
,
1086 Referrer(), base::string16() /* title */,
1087 ui::PAGE_TRANSITION_TYPED
,
1088 false /* is_renderer_init */);
1089 RenderFrameHostImpl
* host1
= NavigateToEntry(manager1
, entry1
);
1091 // We should have a pending navigation to the WebUI RenderViewHost.
1092 // It should already have bindings.
1093 EXPECT_EQ(host1
, GetPendingFrameHost(manager1
));
1094 EXPECT_NE(host1
, manager1
->current_frame_host());
1096 host1
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1098 // Commit and ensure we still have bindings.
1099 manager1
->DidNavigateFrame(host1
, true);
1100 SiteInstance
* webui_instance
= host1
->GetSiteInstance();
1101 EXPECT_EQ(host1
, manager1
->current_frame_host());
1103 host1
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1105 // Now simulate clicking a link that opens in a new tab.
1106 scoped_ptr
<TestWebContents
> web_contents2(
1107 TestWebContents::Create(browser_context(), webui_instance
));
1108 RenderFrameHostManager
* manager2
=
1109 web_contents2
->GetRenderManagerForTesting();
1110 // Make sure the new RVH is considered live. This is usually done in
1111 // RenderWidgetHost::Init when opening a new tab from a link.
1112 manager2
->current_host()->CreateRenderView(
1113 base::string16(), -1, MSG_ROUTING_NONE
, -1, false);
1114 EXPECT_TRUE(manager2
->current_host()->IsRenderViewLive());
1116 const GURL
kUrl2("chrome://foo/bar");
1117 NavigationEntryImpl
entry2(NULL
/* instance */, -1 /* page_id */, kUrl2
,
1118 Referrer(), base::string16() /* title */,
1119 ui::PAGE_TRANSITION_LINK
,
1120 true /* is_renderer_init */);
1121 RenderFrameHostImpl
* host2
= NavigateToEntry(manager2
, entry2
);
1123 // No cross-process transition happens because we are already in the right
1124 // SiteInstance. We should grant bindings immediately.
1125 EXPECT_EQ(host2
, manager2
->current_frame_host());
1126 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1127 switches::kEnableBrowserSideNavigation
)) {
1128 EXPECT_TRUE(manager2
->speculative_web_ui());
1130 EXPECT_TRUE(manager2
->pending_web_ui());
1133 host2
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1135 manager2
->DidNavigateFrame(host2
, true);
1138 // Tests that a WebUI is correctly reused between chrome:// pages.
1139 TEST_F(RenderFrameHostManagerTest
, WebUIWasReused
) {
1140 set_should_create_webui(true);
1142 // Navigate to a WebUI page.
1143 const GURL
kUrl1("chrome://foo");
1144 contents()->NavigateAndCommit(kUrl1
);
1145 RenderFrameHostManager
* manager
=
1146 main_test_rfh()->frame_tree_node()->render_manager();
1147 WebUIImpl
* web_ui
= manager
->web_ui();
1148 EXPECT_TRUE(web_ui
);
1150 // Navigate to another WebUI page which should be same-site and keep the
1152 const GURL
kUrl2("chrome://foo/bar");
1153 contents()->NavigateAndCommit(kUrl2
);
1154 EXPECT_EQ(web_ui
, manager
->web_ui());
1157 // Tests that a WebUI is correctly cleaned up when navigating from a chrome://
1158 // page to a non-chrome:// page.
1159 TEST_F(RenderFrameHostManagerTest
, WebUIWasCleared
) {
1160 set_should_create_webui(true);
1162 // Navigate to a WebUI page.
1163 const GURL
kUrl1("chrome://foo");
1164 contents()->NavigateAndCommit(kUrl1
);
1165 EXPECT_TRUE(main_test_rfh()->frame_tree_node()->render_manager()->web_ui());
1167 // Navigate to a non-WebUI page.
1168 const GURL
kUrl2("http://www.google.com");
1169 contents()->NavigateAndCommit(kUrl2
);
1170 EXPECT_FALSE(main_test_rfh()->frame_tree_node()->render_manager()->web_ui());
1173 // Tests that we don't end up in an inconsistent state if a page does a back and
1174 // then reload. http://crbug.com/51680
1175 // Also tests that only user-gesture navigations can interrupt cross-process
1176 // navigations. http://crbug.com/75195
1177 TEST_F(RenderFrameHostManagerTest
, PageDoesBackAndReload
) {
1178 const GURL
kUrl1("http://www.google.com/");
1179 const GURL
kUrl2("http://www.evil-site.com/");
1181 // Navigate to a safe site, then an evil site.
1182 // This will switch RenderFrameHosts. We cannot assert that the first and
1183 // second RFHs are different, though, because the first one may be promptly
1185 contents()->NavigateAndCommit(kUrl1
);
1186 contents()->NavigateAndCommit(kUrl2
);
1187 TestRenderFrameHost
* evil_rfh
= contents()->GetMainFrame();
1189 // Now let's simulate the evil page calling history.back().
1190 contents()->OnGoToEntryAtOffset(-1);
1191 contents()->GetMainFrame()->PrepareForCommit();
1192 // We should have a new pending RFH.
1193 // Note that in this case, the navigation has not committed, so evil_rfh will
1194 // not be deleted yet.
1195 EXPECT_NE(evil_rfh
, contents()->GetPendingMainFrame());
1196 EXPECT_NE(evil_rfh
->GetRenderViewHost(),
1197 contents()->GetPendingMainFrame()->GetRenderViewHost());
1199 // Before that RFH has committed, the evil page reloads itself.
1200 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1202 params
.nav_entry_id
= 0;
1203 params
.did_create_new_entry
= false;
1205 params
.transition
= ui::PAGE_TRANSITION_CLIENT_REDIRECT
;
1206 params
.should_update_history
= false;
1207 params
.gesture
= NavigationGestureAuto
;
1208 params
.was_within_same_page
= false;
1209 params
.is_post
= false;
1210 params
.page_state
= PageState::CreateFromURL(kUrl2
);
1212 contents()->GetFrameTree()->root()->navigator()->DidNavigate(evil_rfh
,
1215 // That should NOT have cancelled the pending RFH, because the reload did
1216 // not have a user gesture. Thus, the pending back navigation will still
1217 // eventually commit.
1218 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
1219 pending_render_view_host() != NULL
);
1220 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->pending_frame_host() !=
1223 contents()->GetRenderManagerForTesting()->current_frame_host());
1224 EXPECT_EQ(evil_rfh
->GetRenderViewHost(),
1225 contents()->GetRenderManagerForTesting()->current_host());
1227 // Also we should not have a pending navigation entry.
1228 EXPECT_TRUE(contents()->GetController().GetPendingEntry() == NULL
);
1229 NavigationEntry
* entry
= contents()->GetController().GetVisibleEntry();
1230 ASSERT_TRUE(entry
!= NULL
);
1231 EXPECT_EQ(kUrl2
, entry
->GetURL());
1233 // Now do the same but as a user gesture.
1234 params
.gesture
= NavigationGestureUser
;
1235 contents()->GetFrameTree()->root()->navigator()->DidNavigate(evil_rfh
,
1238 // User navigation should have cancelled the pending RFH.
1239 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
1240 pending_render_view_host() == NULL
);
1241 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->pending_frame_host() ==
1244 contents()->GetRenderManagerForTesting()->current_frame_host());
1245 EXPECT_EQ(evil_rfh
->GetRenderViewHost(),
1246 contents()->GetRenderManagerForTesting()->current_host());
1248 // Also we should not have a pending navigation entry.
1249 EXPECT_TRUE(contents()->GetController().GetPendingEntry() == NULL
);
1250 entry
= contents()->GetController().GetVisibleEntry();
1251 ASSERT_TRUE(entry
!= NULL
);
1252 EXPECT_EQ(kUrl2
, entry
->GetURL());
1255 // Ensure that we can go back and forward even if a SwapOut ACK isn't received.
1256 // See http://crbug.com/93427.
1257 TEST_F(RenderFrameHostManagerTest
, NavigateAfterMissingSwapOutACK
) {
1258 const GURL
kUrl1("http://www.google.com/");
1259 const GURL
kUrl2("http://www.chromium.org/");
1261 // Navigate to two pages.
1262 contents()->NavigateAndCommit(kUrl1
);
1263 TestRenderFrameHost
* rfh1
= main_test_rfh();
1265 // Keep active_frame_count nonzero so that no swapped out frames in
1266 // this SiteInstance get forcefully deleted.
1267 rfh1
->GetSiteInstance()->increment_active_frame_count();
1269 contents()->NavigateAndCommit(kUrl2
);
1270 TestRenderFrameHost
* rfh2
= main_test_rfh();
1271 rfh2
->GetSiteInstance()->increment_active_frame_count();
1273 // Now go back, but suppose the SwapOut_ACK isn't received. This shouldn't
1274 // happen, but we have seen it when going back quickly across many entries
1275 // (http://crbug.com/93427).
1276 contents()->GetController().GoBack();
1277 EXPECT_TRUE(rfh2
->IsWaitingForBeforeUnloadACK());
1278 contents()->GetMainFrame()->PrepareForCommit();
1279 EXPECT_FALSE(rfh2
->IsWaitingForBeforeUnloadACK());
1281 // The back navigation commits.
1282 const NavigationEntry
* entry1
= contents()->GetController().GetPendingEntry();
1283 rfh1
->SendNavigate(entry1
->GetPageID(), entry1
->GetUniqueID(), false,
1285 EXPECT_TRUE(rfh2
->IsWaitingForUnloadACK());
1286 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh2
->rfh_state());
1288 // We should be able to navigate forward.
1289 contents()->GetController().GoForward();
1290 contents()->GetMainFrame()->PrepareForCommit();
1291 const NavigationEntry
* entry2
= contents()->GetController().GetPendingEntry();
1292 rfh2
->SendNavigate(entry2
->GetPageID(), entry2
->GetUniqueID(), false,
1294 EXPECT_EQ(rfh2
, main_test_rfh());
1295 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
1296 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
1297 rfh1
->OnSwappedOut();
1298 EXPECT_TRUE(rfh1
->is_swapped_out());
1299 EXPECT_EQ(RenderFrameHostImpl::STATE_SWAPPED_OUT
, rfh1
->rfh_state());
1302 // Test that we create swapped out RFHs for the opener chain when navigating an
1303 // opened tab cross-process. This allows us to support certain cross-process
1304 // JavaScript calls (http://crbug.com/99202).
1305 TEST_F(RenderFrameHostManagerTest
, CreateSwappedOutOpenerRFHs
) {
1306 const GURL
kUrl1("http://www.google.com/");
1307 const GURL
kUrl2("http://www.chromium.org/");
1308 const GURL
kChromeUrl("chrome://foo");
1310 // Navigate to an initial URL.
1311 contents()->NavigateAndCommit(kUrl1
);
1312 RenderFrameHostManager
* manager
= contents()->GetRenderManagerForTesting();
1313 TestRenderFrameHost
* rfh1
= main_test_rfh();
1314 TestRenderViewHost
* rvh1
= test_rvh();
1316 // Create 2 new tabs and simulate them being the opener chain for the main
1317 // tab. They should be in the same SiteInstance.
1318 scoped_ptr
<TestWebContents
> opener1(
1319 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1320 RenderFrameHostManager
* opener1_manager
=
1321 opener1
->GetRenderManagerForTesting();
1322 contents()->SetOpener(opener1
.get());
1324 scoped_ptr
<TestWebContents
> opener2(
1325 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1326 RenderFrameHostManager
* opener2_manager
=
1327 opener2
->GetRenderManagerForTesting();
1328 opener1
->SetOpener(opener2
.get());
1330 // Navigate to a cross-site URL (different SiteInstance but same
1331 // BrowsingInstance).
1332 contents()->NavigateAndCommit(kUrl2
);
1333 TestRenderFrameHost
* rfh2
= main_test_rfh();
1334 TestRenderViewHost
* rvh2
= test_rvh();
1335 EXPECT_NE(rfh1
->GetSiteInstance(), rfh2
->GetSiteInstance());
1336 EXPECT_TRUE(rfh1
->GetSiteInstance()->IsRelatedSiteInstance(
1337 rfh2
->GetSiteInstance()));
1339 // Ensure rvh1 is placed on swapped out list of the current tab.
1340 EXPECT_TRUE(manager
->IsOnSwappedOutList(rfh1
));
1341 EXPECT_TRUE(manager
->IsRVHOnSwappedOutList(rvh1
));
1343 manager
->GetRenderFrameProxyHost(rfh1
->GetSiteInstance())
1344 ->render_frame_host());
1346 manager
->GetSwappedOutRenderViewHost(rvh1
->GetSiteInstance()));
1348 // Ensure a swapped out RFH and RFH is created in the first opener tab.
1349 RenderFrameProxyHost
* opener1_proxy
=
1350 opener1_manager
->GetRenderFrameProxyHost(rfh2
->GetSiteInstance());
1351 RenderFrameHostImpl
* opener1_rfh
= opener1_proxy
->render_frame_host();
1352 TestRenderViewHost
* opener1_rvh
= static_cast<TestRenderViewHost
*>(
1353 opener1_manager
->GetSwappedOutRenderViewHost(rvh2
->GetSiteInstance()));
1354 EXPECT_TRUE(opener1_manager
->IsOnSwappedOutList(opener1_rfh
));
1355 EXPECT_TRUE(opener1_manager
->IsRVHOnSwappedOutList(opener1_rvh
));
1356 EXPECT_TRUE(opener1_rfh
->is_swapped_out());
1357 EXPECT_FALSE(opener1_rvh
->is_active());
1359 // Ensure a swapped out RFH and RVH is created in the second opener tab.
1360 RenderFrameProxyHost
* opener2_proxy
=
1361 opener2_manager
->GetRenderFrameProxyHost(rfh2
->GetSiteInstance());
1362 RenderFrameHostImpl
* opener2_rfh
= opener2_proxy
->render_frame_host();
1363 TestRenderViewHost
* opener2_rvh
= static_cast<TestRenderViewHost
*>(
1364 opener2_manager
->GetSwappedOutRenderViewHost(rvh2
->GetSiteInstance()));
1365 EXPECT_TRUE(opener2_manager
->IsOnSwappedOutList(opener2_rfh
));
1366 EXPECT_TRUE(opener2_manager
->IsRVHOnSwappedOutList(opener2_rvh
));
1367 EXPECT_TRUE(opener2_rfh
->is_swapped_out());
1368 EXPECT_FALSE(opener2_rvh
->is_active());
1370 // Navigate to a cross-BrowsingInstance URL.
1371 contents()->NavigateAndCommit(kChromeUrl
);
1372 TestRenderFrameHost
* rfh3
= main_test_rfh();
1373 EXPECT_NE(rfh1
->GetSiteInstance(), rfh3
->GetSiteInstance());
1374 EXPECT_FALSE(rfh1
->GetSiteInstance()->IsRelatedSiteInstance(
1375 rfh3
->GetSiteInstance()));
1377 // No scripting is allowed across BrowsingInstances, so we should not create
1378 // swapped out RVHs for the opener chain in this case.
1379 EXPECT_FALSE(opener1_manager
->GetRenderFrameProxyHost(
1380 rfh3
->GetSiteInstance()));
1381 EXPECT_FALSE(opener1_manager
->GetSwappedOutRenderViewHost(
1382 rfh3
->GetSiteInstance()));
1383 EXPECT_FALSE(opener2_manager
->GetRenderFrameProxyHost(
1384 rfh3
->GetSiteInstance()));
1385 EXPECT_FALSE(opener2_manager
->GetSwappedOutRenderViewHost(
1386 rfh3
->GetSiteInstance()));
1389 // Test that a page can disown the opener of the WebContents.
1390 TEST_F(RenderFrameHostManagerTest
, DisownOpener
) {
1391 const GURL
kUrl1("http://www.google.com/");
1392 const GURL
kUrl2("http://www.chromium.org/");
1394 // Navigate to an initial URL.
1395 contents()->NavigateAndCommit(kUrl1
);
1396 TestRenderFrameHost
* rfh1
= main_test_rfh();
1398 // Create a new tab and simulate having it be the opener for the main tab.
1399 scoped_ptr
<TestWebContents
> opener1(
1400 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1401 contents()->SetOpener(opener1
.get());
1402 EXPECT_TRUE(contents()->HasOpener());
1404 // Navigate to a cross-site URL (different SiteInstance but same
1405 // BrowsingInstance).
1406 contents()->NavigateAndCommit(kUrl2
);
1407 TestRenderFrameHost
* rfh2
= main_test_rfh();
1408 EXPECT_NE(rfh1
->GetSiteInstance(), rfh2
->GetSiteInstance());
1410 // Disown the opener from rfh2.
1411 rfh2
->DidDisownOpener();
1413 // Ensure the opener is cleared.
1414 EXPECT_FALSE(contents()->HasOpener());
1417 // Test that a page can disown a same-site opener of the WebContents.
1418 TEST_F(RenderFrameHostManagerTest
, DisownSameSiteOpener
) {
1419 const GURL
kUrl1("http://www.google.com/");
1421 // Navigate to an initial URL.
1422 contents()->NavigateAndCommit(kUrl1
);
1423 TestRenderFrameHost
* rfh1
= main_test_rfh();
1425 // Create a new tab and simulate having it be the opener for the main tab.
1426 scoped_ptr
<TestWebContents
> opener1(
1427 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1428 contents()->SetOpener(opener1
.get());
1429 EXPECT_TRUE(contents()->HasOpener());
1431 // Disown the opener from rfh1.
1432 rfh1
->DidDisownOpener();
1434 // Ensure the opener is cleared even if it is in the same process.
1435 EXPECT_FALSE(contents()->HasOpener());
1438 // Test that a page can disown the opener just as a cross-process navigation is
1440 TEST_F(RenderFrameHostManagerTest
, DisownOpenerDuringNavigation
) {
1441 const GURL
kUrl1("http://www.google.com/");
1442 const GURL
kUrl2("http://www.chromium.org/");
1444 // Navigate to an initial URL.
1445 contents()->NavigateAndCommit(kUrl1
);
1446 TestRenderFrameHost
* rfh1
= main_test_rfh();
1448 // Create a new tab and simulate having it be the opener for the main tab.
1449 scoped_ptr
<TestWebContents
> opener1(
1450 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1451 contents()->SetOpener(opener1
.get());
1452 EXPECT_TRUE(contents()->HasOpener());
1454 // Navigate to a cross-site URL (different SiteInstance but same
1455 // BrowsingInstance).
1456 contents()->NavigateAndCommit(kUrl2
);
1457 TestRenderFrameHost
* rfh2
= main_test_rfh();
1458 EXPECT_NE(rfh1
->GetSiteInstance(), rfh2
->GetSiteInstance());
1460 // Start a back navigation so that rfh1 becomes the pending RFH.
1461 contents()->GetController().GoBack();
1462 contents()->GetMainFrame()->PrepareForCommit();
1464 // Disown the opener from rfh2.
1465 rfh2
->DidDisownOpener();
1467 // Ensure the opener is cleared.
1468 EXPECT_FALSE(contents()->HasOpener());
1470 // The back navigation commits.
1471 const NavigationEntry
* entry1
= contents()->GetController().GetPendingEntry();
1472 rfh1
->SendNavigate(entry1
->GetPageID(), entry1
->GetUniqueID(), false,
1475 // Ensure the opener is still cleared.
1476 EXPECT_FALSE(contents()->HasOpener());
1479 // Test that a page can disown the opener just after a cross-process navigation
1481 TEST_F(RenderFrameHostManagerTest
, DisownOpenerAfterNavigation
) {
1482 const GURL
kUrl1("http://www.google.com/");
1483 const GURL
kUrl2("http://www.chromium.org/");
1485 // Navigate to an initial URL.
1486 contents()->NavigateAndCommit(kUrl1
);
1487 TestRenderFrameHost
* rfh1
= main_test_rfh();
1489 // Create a new tab and simulate having it be the opener for the main tab.
1490 scoped_ptr
<TestWebContents
> opener1(
1491 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1492 contents()->SetOpener(opener1
.get());
1493 EXPECT_TRUE(contents()->HasOpener());
1495 // Navigate to a cross-site URL (different SiteInstance but same
1496 // BrowsingInstance).
1497 contents()->NavigateAndCommit(kUrl2
);
1498 TestRenderFrameHost
* rfh2
= main_test_rfh();
1499 EXPECT_NE(rfh1
->GetSiteInstance(), rfh2
->GetSiteInstance());
1501 // Commit a back navigation before the DidDisownOpener message arrives.
1502 // rfh1 will be kept alive because of the opener tab.
1503 contents()->GetController().GoBack();
1504 contents()->GetMainFrame()->PrepareForCommit();
1505 const NavigationEntry
* entry1
= contents()->GetController().GetPendingEntry();
1506 rfh1
->SendNavigate(entry1
->GetPageID(), entry1
->GetUniqueID(), false,
1509 // Disown the opener from rfh2.
1510 rfh2
->DidDisownOpener();
1511 EXPECT_FALSE(contents()->HasOpener());
1514 // Test that we clean up swapped out RenderViewHosts when a process hosting
1515 // those associated RenderViews crashes. http://crbug.com/258993
1516 TEST_F(RenderFrameHostManagerTest
, CleanUpSwappedOutRVHOnProcessCrash
) {
1517 const GURL
kUrl1("http://www.google.com/");
1518 const GURL
kUrl2("http://www.chromium.org/");
1520 // Navigate to an initial URL.
1521 contents()->NavigateAndCommit(kUrl1
);
1522 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1524 // Create a new tab as an opener for the main tab.
1525 scoped_ptr
<TestWebContents
> opener1(
1526 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1527 RenderFrameHostManager
* opener1_manager
=
1528 opener1
->GetRenderManagerForTesting();
1529 contents()->SetOpener(opener1
.get());
1531 // Make sure the new opener RVH is considered live.
1532 opener1_manager
->current_host()->CreateRenderView(
1533 base::string16(), -1, MSG_ROUTING_NONE
, -1, false);
1534 EXPECT_TRUE(opener1_manager
->current_host()->IsRenderViewLive());
1535 EXPECT_TRUE(opener1_manager
->current_frame_host()->IsRenderFrameLive());
1537 // Use a cross-process navigation in the opener to swap out the old RVH.
1539 opener1_manager
->GetSwappedOutRenderViewHost(rfh1
->GetSiteInstance()));
1540 opener1
->NavigateAndCommit(kUrl2
);
1542 opener1_manager
->GetSwappedOutRenderViewHost(rfh1
->GetSiteInstance()));
1544 // Fake a process crash.
1545 rfh1
->GetProcess()->SimulateCrash();
1547 // Ensure that the RenderFrameProxyHost stays around and the RenderFrameProxy
1549 RenderFrameProxyHost
* render_frame_proxy_host
=
1550 opener1_manager
->GetRenderFrameProxyHost(rfh1
->GetSiteInstance());
1551 EXPECT_TRUE(render_frame_proxy_host
);
1552 EXPECT_FALSE(render_frame_proxy_host
->is_render_frame_proxy_live());
1554 // Expect the swapped out RVH to exist.
1556 opener1_manager
->GetSwappedOutRenderViewHost(rfh1
->GetSiteInstance()));
1558 // Reload the initial tab. This should recreate the opener's swapped out RVH
1559 // in the original SiteInstance.
1560 contents()->GetController().Reload(true);
1561 contents()->GetMainFrame()->PrepareForCommit();
1562 EXPECT_EQ(opener1_manager
->GetSwappedOutRenderViewHost(
1563 rfh1
->GetSiteInstance())->GetRoutingID(),
1564 contents()->GetMainFrame()->GetRenderViewHost()->opener_route_id());
1567 // Test that RenderViewHosts created for WebUI navigations are properly
1568 // granted WebUI bindings even if an unprivileged swapped out RenderViewHost
1569 // is in the same process (http://crbug.com/79918).
1570 TEST_F(RenderFrameHostManagerTest
, EnableWebUIWithSwappedOutOpener
) {
1571 set_should_create_webui(true);
1572 const GURL
kSettingsUrl("chrome://chrome/settings");
1573 const GURL
kPluginUrl("chrome://plugins");
1575 // Navigate to an initial WebUI URL.
1576 contents()->NavigateAndCommit(kSettingsUrl
);
1578 // Ensure the RVH has WebUI bindings.
1579 TestRenderViewHost
* rvh1
= test_rvh();
1580 EXPECT_TRUE(rvh1
->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1582 // Create a new tab and simulate it being the opener for the main
1583 // tab. It should be in the same SiteInstance.
1584 scoped_ptr
<TestWebContents
> opener1(
1585 TestWebContents::Create(browser_context(), rvh1
->GetSiteInstance()));
1586 RenderFrameHostManager
* opener1_manager
=
1587 opener1
->GetRenderManagerForTesting();
1588 contents()->SetOpener(opener1
.get());
1590 // Navigate to a different WebUI URL (different SiteInstance, same
1591 // BrowsingInstance).
1592 contents()->NavigateAndCommit(kPluginUrl
);
1593 TestRenderViewHost
* rvh2
= test_rvh();
1594 EXPECT_NE(rvh1
->GetSiteInstance(), rvh2
->GetSiteInstance());
1595 EXPECT_TRUE(rvh1
->GetSiteInstance()->IsRelatedSiteInstance(
1596 rvh2
->GetSiteInstance()));
1598 // Ensure a swapped out RFH and RVH is created in the first opener tab.
1599 RenderFrameProxyHost
* opener1_proxy
=
1600 opener1_manager
->GetRenderFrameProxyHost(rvh2
->GetSiteInstance());
1601 RenderFrameHostImpl
* opener1_rfh
= opener1_proxy
->render_frame_host();
1602 TestRenderViewHost
* opener1_rvh
= static_cast<TestRenderViewHost
*>(
1603 opener1_manager
->GetSwappedOutRenderViewHost(rvh2
->GetSiteInstance()));
1604 EXPECT_TRUE(opener1_manager
->IsOnSwappedOutList(opener1_rfh
));
1605 EXPECT_TRUE(opener1_manager
->IsRVHOnSwappedOutList(opener1_rvh
));
1606 EXPECT_TRUE(opener1_rfh
->is_swapped_out());
1607 EXPECT_FALSE(opener1_rvh
->is_active());
1609 // Ensure the new RVH has WebUI bindings.
1610 EXPECT_TRUE(rvh2
->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1613 // Test that we reuse the same guest SiteInstance if we navigate across sites.
1614 TEST_F(RenderFrameHostManagerTest
, NoSwapOnGuestNavigations
) {
1615 TestNotificationTracker notifications
;
1617 GURL
guest_url(std::string(kGuestScheme
).append("://abc123"));
1618 SiteInstance
* instance
=
1619 SiteInstance::CreateForURL(browser_context(), guest_url
);
1620 scoped_ptr
<TestWebContents
> web_contents(
1621 TestWebContents::Create(browser_context(), instance
));
1623 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
1625 RenderFrameHostImpl
* host
= NULL
;
1627 // 1) The first navigation. --------------------------
1628 const GURL
kUrl1("http://www.google.com/");
1629 NavigationEntryImpl
entry1(
1630 NULL
/* instance */, -1 /* page_id */, kUrl1
, Referrer(),
1631 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED
,
1632 false /* is_renderer_init */);
1633 host
= NavigateToEntry(manager
, entry1
);
1635 // The RenderFrameHost created in Init will be reused.
1636 EXPECT_TRUE(host
== manager
->current_frame_host());
1637 EXPECT_FALSE(manager
->pending_frame_host());
1638 EXPECT_EQ(manager
->current_frame_host()->GetSiteInstance(), instance
);
1641 manager
->DidNavigateFrame(host
, true);
1642 // Commit to SiteInstance should be delayed until RenderFrame commit.
1643 EXPECT_EQ(host
, manager
->current_frame_host());
1645 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
1647 // 2) Navigate to a different domain. -------------------------
1648 // Guests stay in the same process on navigation.
1649 const GURL
kUrl2("http://www.chromium.org");
1650 NavigationEntryImpl
entry2(
1651 NULL
/* instance */, -1 /* page_id */, kUrl2
,
1652 Referrer(kUrl1
, blink::WebReferrerPolicyDefault
),
1653 base::string16() /* title */, ui::PAGE_TRANSITION_LINK
,
1654 true /* is_renderer_init */);
1655 host
= NavigateToEntry(manager
, entry2
);
1657 // The RenderFrameHost created in Init will be reused.
1658 EXPECT_EQ(host
, manager
->current_frame_host());
1659 EXPECT_FALSE(manager
->pending_frame_host());
1662 manager
->DidNavigateFrame(host
, true);
1663 EXPECT_EQ(host
, manager
->current_frame_host());
1665 EXPECT_EQ(host
->GetSiteInstance(), instance
);
1668 // Test that we cancel a pending RVH if we close the tab while it's pending.
1669 // http://crbug.com/294697.
1670 TEST_F(RenderFrameHostManagerTest
, NavigateWithEarlyClose
) {
1671 TestNotificationTracker notifications
;
1673 SiteInstance
* instance
= SiteInstance::Create(browser_context());
1675 BeforeUnloadFiredWebContentsDelegate delegate
;
1676 scoped_ptr
<TestWebContents
> web_contents(
1677 TestWebContents::Create(browser_context(), instance
));
1678 web_contents
->SetDelegate(&delegate
);
1679 notifications
.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED
,
1680 Source
<WebContents
>(web_contents
.get()));
1682 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
1684 // 1) The first navigation. --------------------------
1685 const GURL
kUrl1("http://www.google.com/");
1686 NavigationEntryImpl
entry1(NULL
/* instance */, -1 /* page_id */, kUrl1
,
1687 Referrer(), base::string16() /* title */,
1688 ui::PAGE_TRANSITION_TYPED
,
1689 false /* is_renderer_init */);
1690 RenderFrameHostImpl
* host
= NavigateToEntry(manager
, entry1
);
1692 // The RenderFrameHost created in Init will be reused.
1693 EXPECT_EQ(host
, manager
->current_frame_host());
1694 EXPECT_FALSE(GetPendingFrameHost(manager
));
1696 // We should observe a notification.
1698 notifications
.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED
));
1699 notifications
.Reset();
1702 manager
->DidNavigateFrame(host
, true);
1704 // Commit to SiteInstance should be delayed until RenderFrame commits.
1705 EXPECT_EQ(host
, manager
->current_frame_host());
1706 EXPECT_FALSE(host
->GetSiteInstance()->HasSite());
1707 host
->GetSiteInstance()->SetSite(kUrl1
);
1709 // 2) Cross-site navigate to next site. -------------------------
1710 const GURL
kUrl2("http://www.example.com");
1711 NavigationEntryImpl
entry2(
1712 NULL
/* instance */, -1 /* page_id */, kUrl2
, Referrer(),
1713 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED
,
1714 false /* is_renderer_init */);
1715 RenderFrameHostImpl
* host2
= NavigateToEntry(manager
, entry2
);
1717 // A new RenderFrameHost should be created.
1718 ASSERT_EQ(host2
, GetPendingFrameHost(manager
));
1719 EXPECT_NE(host2
, host
);
1721 EXPECT_EQ(host
, manager
->current_frame_host());
1722 EXPECT_FALSE(manager
->current_frame_host()->is_swapped_out());
1723 EXPECT_EQ(host2
, GetPendingFrameHost(manager
));
1725 // 3) Close the tab. -------------------------
1726 notifications
.ListenFor(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED
,
1727 Source
<RenderWidgetHost
>(host2
->render_view_host()));
1728 manager
->OnBeforeUnloadACK(false, true, base::TimeTicks());
1731 notifications
.Check1AndReset(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED
));
1732 EXPECT_FALSE(GetPendingFrameHost(manager
));
1733 EXPECT_EQ(host
, manager
->current_frame_host());
1736 TEST_F(RenderFrameHostManagerTest
, CloseWithPendingWhileUnresponsive
) {
1737 const GURL
kUrl1("http://www.google.com/");
1738 const GURL
kUrl2("http://www.chromium.org/");
1740 CloseWebContentsDelegate close_delegate
;
1741 contents()->SetDelegate(&close_delegate
);
1743 // Navigate to the first page.
1744 contents()->NavigateAndCommit(kUrl1
);
1745 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1747 // Start to close the tab, but assume it's unresponsive.
1748 rfh1
->render_view_host()->ClosePage();
1749 EXPECT_TRUE(rfh1
->render_view_host()->is_waiting_for_close_ack());
1751 // Start a navigation to a new site.
1752 controller().LoadURL(
1753 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1754 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1755 switches::kEnableBrowserSideNavigation
)) {
1756 rfh1
->PrepareForCommit();
1758 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1760 // Simulate the unresponsiveness timer. The tab should close.
1761 contents()->RendererUnresponsive(rfh1
->render_view_host());
1762 EXPECT_TRUE(close_delegate
.is_closed());
1765 // Tests that the RenderFrameHost is properly deleted when the SwapOutACK is
1766 // received. (SwapOut and the corresponding ACK always occur after commit.)
1767 // Also tests that an early SwapOutACK is properly ignored.
1768 TEST_F(RenderFrameHostManagerTest
, DeleteFrameAfterSwapOutACK
) {
1769 const GURL
kUrl1("http://www.google.com/");
1770 const GURL
kUrl2("http://www.chromium.org/");
1772 // Navigate to the first page.
1773 contents()->NavigateAndCommit(kUrl1
);
1774 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1775 RenderFrameHostDeletedObserver
rfh_deleted_observer(rfh1
);
1776 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1778 // Navigate to new site, simulating onbeforeunload approval.
1779 controller().LoadURL(
1780 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1781 int entry_id
= controller().GetPendingEntry()->GetUniqueID();
1782 contents()->GetMainFrame()->PrepareForCommit();
1783 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1784 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1785 TestRenderFrameHost
* rfh2
= contents()->GetPendingMainFrame();
1787 // Simulate the swap out ack, unexpectedly early (before commit). It should
1789 rfh1
->OnSwappedOut();
1790 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1791 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1793 // The new page commits.
1794 contents()->TestDidNavigate(rfh2
, 1, entry_id
, true, kUrl2
,
1795 ui::PAGE_TRANSITION_TYPED
);
1796 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1797 EXPECT_EQ(rfh2
, contents()->GetMainFrame());
1798 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL
);
1799 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
1800 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
1802 rfh1
->frame_tree_node()->render_manager()->IsPendingDeletion(rfh1
));
1804 // Simulate the swap out ack.
1805 rfh1
->OnSwappedOut();
1807 // rfh1 should have been deleted.
1808 EXPECT_TRUE(rfh_deleted_observer
.deleted());
1812 // Tests that the RenderFrameHost is properly swapped out when the SwapOut ACK
1813 // is received. (SwapOut and the corresponding ACK always occur after commit.)
1814 TEST_F(RenderFrameHostManagerTest
, SwapOutFrameAfterSwapOutACK
) {
1815 const GURL
kUrl1("http://www.google.com/");
1816 const GURL
kUrl2("http://www.chromium.org/");
1818 // Navigate to the first page.
1819 contents()->NavigateAndCommit(kUrl1
);
1820 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1821 RenderFrameHostDeletedObserver
rfh_deleted_observer(rfh1
);
1822 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1824 // Increment the number of active frames in SiteInstanceImpl so that rfh1 is
1825 // not deleted on swap out.
1826 rfh1
->GetSiteInstance()->increment_active_frame_count();
1828 // Navigate to new site, simulating onbeforeunload approval.
1829 controller().LoadURL(
1830 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1831 int entry_id
= controller().GetPendingEntry()->GetUniqueID();
1832 contents()->GetMainFrame()->PrepareForCommit();
1833 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1834 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1835 TestRenderFrameHost
* rfh2
= contents()->GetPendingMainFrame();
1837 // The new page commits.
1838 contents()->TestDidNavigate(rfh2
, 1, entry_id
, true, kUrl2
,
1839 ui::PAGE_TRANSITION_TYPED
);
1840 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1841 EXPECT_EQ(rfh2
, contents()->GetMainFrame());
1842 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL
);
1843 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
1844 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
1846 // Simulate the swap out ack.
1847 rfh1
->OnSwappedOut();
1849 // rfh1 should be swapped out.
1850 EXPECT_FALSE(rfh_deleted_observer
.deleted());
1851 EXPECT_TRUE(rfh1
->is_swapped_out());
1854 // Test that the RenderViewHost is properly swapped out if a navigation in the
1855 // new renderer commits before sending the SwapOut message to the old renderer.
1856 // This simulates a cross-site navigation to a synchronously committing URL
1857 // (e.g., a data URL) and ensures it works properly.
1858 TEST_F(RenderFrameHostManagerTest
,
1859 CommitNewNavigationBeforeSendingSwapOut
) {
1860 const GURL
kUrl1("http://www.google.com/");
1861 const GURL
kUrl2("http://www.chromium.org/");
1863 // Navigate to the first page.
1864 contents()->NavigateAndCommit(kUrl1
);
1865 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1866 RenderFrameHostDeletedObserver
rfh_deleted_observer(rfh1
);
1867 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1869 // Increment the number of active frames in SiteInstanceImpl so that rfh1 is
1870 // not deleted on swap out.
1871 rfh1
->GetSiteInstance()->increment_active_frame_count();
1873 // Navigate to new site, simulating onbeforeunload approval.
1874 controller().LoadURL(
1875 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1876 int entry_id
= controller().GetPendingEntry()->GetUniqueID();
1877 rfh1
->PrepareForCommit();
1878 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1879 TestRenderFrameHost
* rfh2
= contents()->GetPendingMainFrame();
1881 // The new page commits.
1882 contents()->TestDidNavigate(rfh2
, 1, entry_id
, true, kUrl2
,
1883 ui::PAGE_TRANSITION_TYPED
);
1884 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1885 EXPECT_EQ(rfh2
, contents()->GetMainFrame());
1886 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL
);
1887 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
1888 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
1890 // Simulate the swap out ack.
1891 rfh1
->OnSwappedOut();
1893 // rfh1 should be swapped out.
1894 EXPECT_FALSE(rfh_deleted_observer
.deleted());
1895 EXPECT_TRUE(rfh1
->is_swapped_out());
1898 // Test that a RenderFrameHost is properly deleted or swapped out when a
1899 // cross-site navigation is cancelled.
1900 TEST_F(RenderFrameHostManagerTest
,
1901 CancelPendingProperlyDeletesOrSwaps
) {
1902 const GURL
kUrl1("http://www.google.com/");
1903 const GURL
kUrl2("http://www.chromium.org/");
1904 RenderFrameHostImpl
* pending_rfh
= NULL
;
1905 base::TimeTicks now
= base::TimeTicks::Now();
1907 // Navigate to the first page.
1908 contents()->NavigateAndCommit(kUrl1
);
1909 TestRenderFrameHost
* rfh1
= main_test_rfh();
1910 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1912 // Navigate to a new site, starting a cross-site navigation.
1913 controller().LoadURL(
1914 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1916 pending_rfh
= contents()->GetFrameTree()->root()->render_manager()
1917 ->pending_frame_host();
1918 RenderFrameHostDeletedObserver
rfh_deleted_observer(pending_rfh
);
1920 // Cancel the navigation by simulating a declined beforeunload dialog.
1921 contents()->GetMainFrame()->OnMessageReceived(
1922 FrameHostMsg_BeforeUnload_ACK(0, false, now
, now
));
1923 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1925 // Since the pending RFH is the only one for the new SiteInstance, it should
1927 EXPECT_TRUE(rfh_deleted_observer
.deleted());
1930 // Start another cross-site navigation.
1931 controller().LoadURL(
1932 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1934 pending_rfh
= contents()->GetFrameTree()->root()->render_manager()
1935 ->pending_frame_host();
1936 RenderFrameHostDeletedObserver
rfh_deleted_observer(pending_rfh
);
1938 // Increment the number of active frames in the new SiteInstance, which will
1939 // cause the pending RFH to be swapped out instead of deleted.
1940 pending_rfh
->GetSiteInstance()->increment_active_frame_count();
1942 contents()->GetMainFrame()->OnMessageReceived(
1943 FrameHostMsg_BeforeUnload_ACK(0, false, now
, now
));
1944 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1945 EXPECT_FALSE(rfh_deleted_observer
.deleted());
1949 // Test that a pending RenderFrameHost in a non-root frame tree node is properly
1950 // deleted when the node is detached. Motivated by http://crbug.com/441357 and
1951 // http://crbug.com/444955.
1952 TEST_F(RenderFrameHostManagerTest
, DetachPendingChild
) {
1953 base::CommandLine::ForCurrentProcess()->AppendSwitch(
1954 switches::kSitePerProcess
);
1956 const GURL
kUrlA("http://www.google.com/");
1957 const GURL
kUrlB("http://webkit.org/");
1959 // Create a page with two child frames.
1960 contents()->NavigateAndCommit(kUrlA
);
1961 contents()->GetMainFrame()->OnCreateChildFrame(
1962 contents()->GetMainFrame()->GetProcess()->GetNextRoutingID(),
1963 std::string("frame_name"), SandboxFlags::NONE
);
1964 contents()->GetMainFrame()->OnCreateChildFrame(
1965 contents()->GetMainFrame()->GetProcess()->GetNextRoutingID(),
1966 std::string("frame_name"), SandboxFlags::NONE
);
1967 RenderFrameHostManager
* root_manager
=
1968 contents()->GetFrameTree()->root()->render_manager();
1969 RenderFrameHostManager
* iframe1
=
1970 contents()->GetFrameTree()->root()->child_at(0)->render_manager();
1971 RenderFrameHostManager
* iframe2
=
1972 contents()->GetFrameTree()->root()->child_at(1)->render_manager();
1974 // 1) The first navigation.
1975 NavigationEntryImpl
entryA(NULL
/* instance */, -1 /* page_id */, kUrlA
,
1976 Referrer(), base::string16() /* title */,
1977 ui::PAGE_TRANSITION_TYPED
,
1978 false /* is_renderer_init */);
1979 RenderFrameHostImpl
* host1
= NavigateToEntry(iframe1
, entryA
);
1981 // The RenderFrameHost created in Init will be reused.
1982 EXPECT_TRUE(host1
== iframe1
->current_frame_host());
1983 EXPECT_FALSE(GetPendingFrameHost(iframe1
));
1986 iframe1
->DidNavigateFrame(host1
, true);
1987 // Commit to SiteInstance should be delayed until RenderFrame commit.
1988 EXPECT_TRUE(host1
== iframe1
->current_frame_host());
1990 EXPECT_TRUE(host1
->GetSiteInstance()->HasSite());
1992 // 2) Cross-site navigate both frames to next site.
1993 NavigationEntryImpl
entryB(NULL
/* instance */, -1 /* page_id */, kUrlB
,
1994 Referrer(kUrlA
, blink::WebReferrerPolicyDefault
),
1995 base::string16() /* title */,
1996 ui::PAGE_TRANSITION_LINK
,
1997 false /* is_renderer_init */);
1998 host1
= NavigateToEntry(iframe1
, entryB
);
1999 RenderFrameHostImpl
* host2
= NavigateToEntry(iframe2
, entryB
);
2001 // A new, pending RenderFrameHost should be created in each FrameTreeNode.
2002 EXPECT_TRUE(GetPendingFrameHost(iframe1
));
2003 EXPECT_TRUE(GetPendingFrameHost(iframe2
));
2004 EXPECT_EQ(host1
, GetPendingFrameHost(iframe1
));
2005 EXPECT_EQ(host2
, GetPendingFrameHost(iframe2
));
2006 EXPECT_TRUE(RenderFrameHostImpl::IsRFHStateActive(
2007 GetPendingFrameHost(iframe1
)->rfh_state()));
2008 EXPECT_TRUE(RenderFrameHostImpl::IsRFHStateActive(
2009 GetPendingFrameHost(iframe2
)->rfh_state()));
2010 EXPECT_NE(GetPendingFrameHost(iframe1
), GetPendingFrameHost(iframe2
));
2011 EXPECT_EQ(GetPendingFrameHost(iframe1
)->GetSiteInstance(),
2012 GetPendingFrameHost(iframe2
)->GetSiteInstance());
2013 EXPECT_NE(iframe1
->current_frame_host(), GetPendingFrameHost(iframe1
));
2014 EXPECT_NE(iframe2
->current_frame_host(), GetPendingFrameHost(iframe2
));
2015 EXPECT_FALSE(contents()->CrossProcessNavigationPending())
2016 << "There should be no top-level pending navigation.";
2018 RenderFrameHostDeletedObserver
delete_watcher1(GetPendingFrameHost(iframe1
));
2019 RenderFrameHostDeletedObserver
delete_watcher2(GetPendingFrameHost(iframe2
));
2020 EXPECT_FALSE(delete_watcher1
.deleted());
2021 EXPECT_FALSE(delete_watcher2
.deleted());
2023 // Keep the SiteInstance alive for testing.
2024 scoped_refptr
<SiteInstanceImpl
> site_instance
=
2025 GetPendingFrameHost(iframe1
)->GetSiteInstance();
2026 EXPECT_TRUE(site_instance
->HasSite());
2027 EXPECT_NE(site_instance
, contents()->GetSiteInstance());
2028 EXPECT_EQ(2U, site_instance
->active_frame_count());
2030 // Proxies should exist.
2032 root_manager
->GetRenderFrameProxyHost(site_instance
.get()));
2034 iframe1
->GetRenderFrameProxyHost(site_instance
.get()));
2036 iframe2
->GetRenderFrameProxyHost(site_instance
.get()));
2038 // Detach the first child FrameTreeNode. This should kill the pending host but
2039 // not yet destroy proxies in |site_instance| since the other child remains.
2040 iframe1
->current_frame_host()->OnMessageReceived(
2041 FrameHostMsg_Detach(iframe1
->current_frame_host()->GetRoutingID()));
2042 iframe1
= NULL
; // Was just destroyed.
2044 EXPECT_TRUE(delete_watcher1
.deleted());
2045 EXPECT_FALSE(delete_watcher2
.deleted());
2046 EXPECT_EQ(1U, site_instance
->active_frame_count());
2048 // Proxies should still exist.
2050 root_manager
->GetRenderFrameProxyHost(site_instance
.get()));
2052 iframe2
->GetRenderFrameProxyHost(site_instance
.get()));
2054 // Detach the second child FrameTreeNode. This should trigger cleanup of
2055 // RenderFrameProxyHosts in |site_instance|.
2056 iframe2
->current_frame_host()->OnMessageReceived(
2057 FrameHostMsg_Detach(iframe2
->current_frame_host()->GetRoutingID()));
2058 iframe2
= NULL
; // Was just destroyed.
2060 EXPECT_TRUE(delete_watcher1
.deleted());
2061 EXPECT_TRUE(delete_watcher2
.deleted());
2063 EXPECT_EQ(0U, site_instance
->active_frame_count());
2065 root_manager
->GetRenderFrameProxyHost(site_instance
.get()))
2066 << "Proxies should have been cleaned up";
2067 EXPECT_TRUE(site_instance
->HasOneRef())
2068 << "This SiteInstance should be destroyable now.";
2071 // Two tabs in the same process crash. The first tab is reloaded, and the second
2072 // tab navigates away without reloading. The second tab's navigation shouldn't
2073 // mess with the first tab's content. Motivated by http://crbug.com/473714.
2074 TEST_F(RenderFrameHostManagerTest
, TwoTabsCrashOneReloadsOneLeaves
) {
2075 base::CommandLine::ForCurrentProcess()->AppendSwitch(
2076 switches::kSitePerProcess
);
2078 const GURL
kUrl1("http://www.google.com/");
2079 const GURL
kUrl2("http://webkit.org/");
2080 const GURL
kUrl3("http://whatwg.org/");
2082 // |contents1| and |contents2| navigate to the same page and then crash.
2083 TestWebContents
* contents1
= contents();
2084 scoped_ptr
<TestWebContents
> contents2(
2085 TestWebContents::Create(browser_context(), contents1
->GetSiteInstance()));
2086 contents1
->NavigateAndCommit(kUrl1
);
2087 contents2
->NavigateAndCommit(kUrl1
);
2088 MockRenderProcessHost
* rph
= contents1
->GetMainFrame()->GetProcess();
2089 EXPECT_EQ(rph
, contents2
->GetMainFrame()->GetProcess());
2090 rph
->SimulateCrash();
2091 EXPECT_FALSE(contents1
->GetMainFrame()->IsRenderFrameLive());
2092 EXPECT_FALSE(contents2
->GetMainFrame()->IsRenderFrameLive());
2093 EXPECT_EQ(contents1
->GetSiteInstance(), contents2
->GetSiteInstance());
2095 // Reload |contents1|.
2096 contents1
->NavigateAndCommit(kUrl1
);
2097 EXPECT_TRUE(contents1
->GetMainFrame()->IsRenderFrameLive());
2098 EXPECT_FALSE(contents2
->GetMainFrame()->IsRenderFrameLive());
2099 EXPECT_EQ(contents1
->GetSiteInstance(), contents2
->GetSiteInstance());
2101 // |contents1| creates an out of process iframe.
2102 contents1
->GetMainFrame()->OnCreateChildFrame(
2103 contents1
->GetMainFrame()->GetProcess()->GetNextRoutingID(),
2104 std::string("frame_name"), SandboxFlags::NONE
);
2105 RenderFrameHostManager
* iframe
=
2106 contents()->GetFrameTree()->root()->child_at(0)->render_manager();
2107 NavigationEntryImpl
entry(NULL
/* instance */, -1 /* page_id */, kUrl2
,
2108 Referrer(kUrl1
, blink::WebReferrerPolicyDefault
),
2109 base::string16() /* title */,
2110 ui::PAGE_TRANSITION_LINK
,
2111 false /* is_renderer_init */);
2112 RenderFrameHostImpl
* cross_site
= NavigateToEntry(iframe
, entry
);
2113 iframe
->DidNavigateFrame(cross_site
, true);
2115 // A proxy to the iframe should now exist in the SiteInstance of the main
2117 EXPECT_NE(cross_site
->GetSiteInstance(), contents1
->GetSiteInstance());
2119 iframe
->GetRenderFrameProxyHost(contents1
->GetSiteInstance()));
2121 iframe
->GetRenderFrameProxyHost(contents2
->GetSiteInstance()));
2123 // Navigate |contents2| away from the sad tab (and thus away from the
2124 // SiteInstance of |contents1|). This should not destroy the proxies needed by
2125 // |contents1| -- that was http://crbug.com/473714.
2126 EXPECT_FALSE(contents2
->GetMainFrame()->IsRenderFrameLive());
2127 contents2
->NavigateAndCommit(kUrl3
);
2128 EXPECT_TRUE(contents2
->GetMainFrame()->IsRenderFrameLive());
2130 iframe
->GetRenderFrameProxyHost(contents1
->GetSiteInstance()));
2132 iframe
->GetRenderFrameProxyHost(contents2
->GetSiteInstance()));
2135 } // namespace content