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/site_isolation_policy.h"
22 #include "content/common/view_messages.h"
23 #include "content/public/browser/notification_details.h"
24 #include "content/public/browser/notification_service.h"
25 #include "content/public/browser/notification_source.h"
26 #include "content/public/browser/notification_types.h"
27 #include "content/public/browser/render_process_host.h"
28 #include "content/public/browser/render_widget_host_iterator.h"
29 #include "content/public/browser/web_contents_delegate.h"
30 #include "content/public/browser/web_contents_observer.h"
31 #include "content/public/browser/web_ui_controller.h"
32 #include "content/public/common/bindings_policy.h"
33 #include "content/public/common/content_switches.h"
34 #include "content/public/common/javascript_message_type.h"
35 #include "content/public/common/url_constants.h"
36 #include "content/public/common/url_utils.h"
37 #include "content/public/test/mock_render_process_host.h"
38 #include "content/public/test/test_notification_tracker.h"
39 #include "content/public/test/test_utils.h"
40 #include "content/test/test_content_browser_client.h"
41 #include "content/test/test_content_client.h"
42 #include "content/test/test_render_frame_host.h"
43 #include "content/test/test_render_view_host.h"
44 #include "content/test/test_web_contents.h"
45 #include "net/base/load_flags.h"
46 #include "testing/gtest/include/gtest/gtest.h"
47 #include "third_party/WebKit/public/web/WebSandboxFlags.h"
48 #include "ui/base/page_transition_types.h"
53 class RenderFrameHostManagerTestWebUIControllerFactory
54 : public WebUIControllerFactory
{
56 RenderFrameHostManagerTestWebUIControllerFactory()
57 : should_create_webui_(false) {
59 ~RenderFrameHostManagerTestWebUIControllerFactory() override
{}
61 void set_should_create_webui(bool should_create_webui
) {
62 should_create_webui_
= should_create_webui
;
65 // WebUIFactory implementation.
66 WebUIController
* CreateWebUIControllerForURL(WebUI
* web_ui
,
67 const GURL
& url
) const override
{
68 if (!(should_create_webui_
&& HasWebUIScheme(url
)))
70 return new WebUIController(web_ui
);
73 WebUI::TypeID
GetWebUIType(BrowserContext
* browser_context
,
74 const GURL
& url
) const override
{
75 return WebUI::kNoWebUI
;
78 bool UseWebUIForURL(BrowserContext
* browser_context
,
79 const GURL
& url
) const override
{
80 return HasWebUIScheme(url
);
83 bool UseWebUIBindingsForURL(BrowserContext
* browser_context
,
84 const GURL
& url
) const override
{
85 return HasWebUIScheme(url
);
89 bool should_create_webui_
;
91 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostManagerTestWebUIControllerFactory
);
94 class BeforeUnloadFiredWebContentsDelegate
: public WebContentsDelegate
{
96 BeforeUnloadFiredWebContentsDelegate() {}
97 ~BeforeUnloadFiredWebContentsDelegate() override
{}
99 void BeforeUnloadFired(WebContents
* web_contents
,
101 bool* proceed_to_fire_unload
) override
{
102 *proceed_to_fire_unload
= proceed
;
106 DISALLOW_COPY_AND_ASSIGN(BeforeUnloadFiredWebContentsDelegate
);
109 class CloseWebContentsDelegate
: public WebContentsDelegate
{
111 CloseWebContentsDelegate() : close_called_(false) {}
112 ~CloseWebContentsDelegate() override
{}
114 void CloseContents(WebContents
* web_contents
) override
{
115 close_called_
= true;
118 bool is_closed() { return close_called_
; }
121 DISALLOW_COPY_AND_ASSIGN(CloseWebContentsDelegate
);
126 // This observer keeps track of the last deleted RenderViewHost to avoid
127 // accessing it and causing use-after-free condition.
128 class RenderViewHostDeletedObserver
: public WebContentsObserver
{
130 RenderViewHostDeletedObserver(RenderViewHost
* rvh
)
131 : WebContentsObserver(WebContents::FromRenderViewHost(rvh
)),
132 process_id_(rvh
->GetProcess()->GetID()),
133 routing_id_(rvh
->GetRoutingID()),
137 void RenderViewDeleted(RenderViewHost
* render_view_host
) override
{
138 if (render_view_host
->GetProcess()->GetID() == process_id_
&&
139 render_view_host
->GetRoutingID() == routing_id_
) {
153 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDeletedObserver
);
156 // This observer keeps track of the last created RenderFrameHost to allow tests
157 // to ensure that no RenderFrameHost objects are created when not expected.
158 class RenderFrameHostCreatedObserver
: public WebContentsObserver
{
160 RenderFrameHostCreatedObserver(WebContents
* web_contents
)
161 : WebContentsObserver(web_contents
),
165 void RenderFrameCreated(RenderFrameHost
* render_frame_host
) override
{
176 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostCreatedObserver
);
179 // This WebContents observer keep track of its RVH change.
180 class RenderViewHostChangedObserver
: public WebContentsObserver
{
182 RenderViewHostChangedObserver(WebContents
* web_contents
)
183 : WebContentsObserver(web_contents
), host_changed_(false) {}
185 // WebContentsObserver.
186 void RenderViewHostChanged(RenderViewHost
* old_host
,
187 RenderViewHost
* new_host
) override
{
188 host_changed_
= true;
191 bool DidHostChange() {
192 bool host_changed
= host_changed_
;
197 void Reset() { host_changed_
= false; }
201 DISALLOW_COPY_AND_ASSIGN(RenderViewHostChangedObserver
);
204 // This observer is used to check whether IPC messages are being filtered for
205 // swapped out RenderFrameHost objects. It observes the plugin crash and favicon
206 // update events, which the FilterMessagesWhileSwappedOut test simulates being
207 // sent. The test is successful if the event is not observed.
208 // See http://crbug.com/351815
209 class PluginFaviconMessageObserver
: public WebContentsObserver
{
211 PluginFaviconMessageObserver(WebContents
* web_contents
)
212 : WebContentsObserver(web_contents
),
213 plugin_crashed_(false),
214 favicon_received_(false) { }
216 void PluginCrashed(const base::FilePath
& plugin_path
,
217 base::ProcessId plugin_pid
) override
{
218 plugin_crashed_
= true;
221 void DidUpdateFaviconURL(const std::vector
<FaviconURL
>& candidates
) override
{
222 favicon_received_
= true;
225 bool plugin_crashed() {
226 return plugin_crashed_
;
229 bool favicon_received() {
230 return favicon_received_
;
234 bool plugin_crashed_
;
235 bool favicon_received_
;
237 DISALLOW_COPY_AND_ASSIGN(PluginFaviconMessageObserver
);
242 class RenderFrameHostManagerTest
: public RenderViewHostImplTestHarness
{
244 void SetUp() override
{
245 RenderViewHostImplTestHarness::SetUp();
246 WebUIControllerFactory::RegisterFactory(&factory_
);
247 #if !defined(OS_ANDROID)
248 ImageTransportFactory::InitializeForUnitTests(
249 make_scoped_ptr(new NoTransportImageTransportFactory
));
253 void TearDown() override
{
254 RenderViewHostImplTestHarness::TearDown();
255 WebUIControllerFactory::UnregisterFactoryForTesting(&factory_
);
256 #if !defined(OS_ANDROID)
257 // RenderWidgetHostView holds on to a reference to SurfaceManager, so it
258 // must be shut down before the ImageTransportFactory.
259 ImageTransportFactory::Terminate();
263 void set_should_create_webui(bool should_create_webui
) {
264 factory_
.set_should_create_webui(should_create_webui
);
267 void NavigateActiveAndCommit(const GURL
& url
) {
268 // Note: we navigate the active RenderFrameHost because previous navigations
269 // won't have committed yet, so NavigateAndCommit does the wrong thing
271 controller().LoadURL(
272 url
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
273 int entry_id
= controller().GetPendingEntry()->GetUniqueID();
275 // Simulate the BeforeUnload_ACK that is received from the current renderer
276 // for a cross-site navigation.
277 // PlzNavigate: it is necessary to call PrepareForCommit before getting the
278 // main and the pending frame because when we are trying to navigate to a
279 // WebUI from a new tab, a RenderFrameHost is created to display it that is
280 // committed immediately (since it is a new tab). Therefore the main frame
281 // is replaced without a pending frame being created, and we don't get the
282 // right values for the RFH to navigate: we try to use the old one that has
283 // been deleted in the meantime.
284 contents()->GetMainFrame()->PrepareForCommit();
286 TestRenderFrameHost
* old_rfh
= contents()->GetMainFrame();
287 TestRenderFrameHost
* active_rfh
= contents()->GetPendingMainFrame()
288 ? contents()->GetPendingMainFrame()
290 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, old_rfh
->rfh_state());
292 // Commit the navigation with a new page ID.
293 int32 max_page_id
= contents()->GetMaxPageIDForSiteInstance(
294 active_rfh
->GetSiteInstance());
296 // Use an observer to avoid accessing a deleted renderer later on when the
297 // state is being checked.
298 RenderFrameDeletedObserver
rfh_observer(old_rfh
);
299 RenderViewHostDeletedObserver
rvh_observer(old_rfh
->GetRenderViewHost());
300 active_rfh
->SendNavigate(max_page_id
+ 1, entry_id
, true, url
);
302 // Make sure that we start to run the unload handler at the time of commit.
303 bool expecting_rfh_shutdown
= false;
304 if (old_rfh
!= active_rfh
&& !rfh_observer
.deleted()) {
305 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
,
306 old_rfh
->rfh_state());
307 if (!old_rfh
->GetSiteInstance()->active_frame_count() ||
308 SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
309 expecting_rfh_shutdown
= true;
311 old_rfh
->frame_tree_node()->render_manager()->IsPendingDeletion(
316 // Simulate the swap out ACK coming from the pending renderer. This should
317 // either shut down the old RFH or leave it in a swapped out state.
318 if (old_rfh
!= active_rfh
) {
319 old_rfh
->OnSwappedOut();
320 if (expecting_rfh_shutdown
) {
321 EXPECT_TRUE(rfh_observer
.deleted());
322 if (!SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
323 EXPECT_TRUE(rvh_observer
.deleted());
326 EXPECT_EQ(RenderFrameHostImpl::STATE_SWAPPED_OUT
,
327 old_rfh
->rfh_state());
330 EXPECT_EQ(active_rfh
, contents()->GetMainFrame());
331 EXPECT_EQ(NULL
, contents()->GetPendingMainFrame());
334 bool ShouldSwapProcesses(RenderFrameHostManager
* manager
,
335 const NavigationEntryImpl
* current_entry
,
336 const NavigationEntryImpl
* new_entry
) const {
338 BrowserContext
* browser_context
=
339 manager
->delegate_
->GetControllerForRenderManager().GetBrowserContext();
340 const GURL
& current_effective_url
= current_entry
?
341 SiteInstanceImpl::GetEffectiveURL(browser_context
,
342 current_entry
->GetURL()) :
343 manager
->render_frame_host_
->GetSiteInstance()->GetSiteURL();
344 bool current_is_view_source_mode
= current_entry
?
345 current_entry
->IsViewSourceMode() : new_entry
->IsViewSourceMode();
346 return manager
->ShouldSwapBrowsingInstancesForNavigation(
347 current_effective_url
,
348 current_is_view_source_mode
,
349 new_entry
->site_instance(),
350 SiteInstanceImpl::GetEffectiveURL(browser_context
, new_entry
->GetURL()),
351 new_entry
->IsViewSourceMode());
354 // Creates a test RenderFrameHost that's swapped out.
355 TestRenderFrameHost
* CreateSwappedOutRenderFrameHost() {
356 const GURL
kChromeURL("chrome://foo");
357 const GURL
kDestUrl("http://www.google.com/");
359 // Navigate our first tab to a chrome url and then to the destination.
360 NavigateActiveAndCommit(kChromeURL
);
361 TestRenderFrameHost
* ntp_rfh
= contents()->GetMainFrame();
363 // Navigate to a cross-site URL.
364 contents()->GetController().LoadURL(
365 kDestUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
366 int entry_id
= contents()->GetController().GetPendingEntry()->GetUniqueID();
367 contents()->GetMainFrame()->PrepareForCommit();
368 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
370 // Manually increase the number of active frames in the
371 // SiteInstance that ntp_rfh belongs to, to prevent it from being
372 // destroyed when it gets swapped out.
373 ntp_rfh
->GetSiteInstance()->increment_active_frame_count();
375 TestRenderFrameHost
* dest_rfh
= contents()->GetPendingMainFrame();
377 EXPECT_NE(ntp_rfh
, dest_rfh
);
379 // BeforeUnload finishes.
380 ntp_rfh
->SendBeforeUnloadACK(true);
382 dest_rfh
->SendNavigate(101, entry_id
, true, kDestUrl
);
383 ntp_rfh
->OnSwappedOut();
385 EXPECT_TRUE(ntp_rfh
->is_swapped_out());
389 // Returns the RenderFrameHost that should be used in the navigation to
391 RenderFrameHostImpl
* NavigateToEntry(
392 RenderFrameHostManager
* manager
,
393 const NavigationEntryImpl
& entry
) {
394 // Tests currently only navigate using main frame FrameNavigationEntries.
395 FrameNavigationEntry
* frame_entry
= entry
.root_node()->frame_entry
.get();
396 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
397 switches::kEnableBrowserSideNavigation
)) {
398 scoped_ptr
<NavigationRequest
> navigation_request
=
399 NavigationRequest::CreateBrowserInitiated(
400 manager
->frame_tree_node_
, frame_entry
->url(),
401 frame_entry
->referrer(), *frame_entry
, entry
,
402 FrameMsg_Navigate_Type::NORMAL
, false, base::TimeTicks::Now(),
403 static_cast<NavigationControllerImpl
*>(&controller()));
404 TestRenderFrameHost
* frame_host
= static_cast<TestRenderFrameHost
*>(
405 manager
->GetFrameHostForNavigation(*navigation_request
));
407 frame_host
->set_pending_commit(true);
411 return manager
->Navigate(frame_entry
->url(), *frame_entry
, entry
);
414 // Returns the pending RenderFrameHost.
415 // PlzNavigate: returns the speculative RenderFrameHost.
416 RenderFrameHostImpl
* GetPendingFrameHost(
417 RenderFrameHostManager
* manager
) {
418 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
419 switches::kEnableBrowserSideNavigation
)) {
420 return manager
->speculative_render_frame_host_
.get();
422 return manager
->pending_frame_host();
426 RenderFrameHostManagerTestWebUIControllerFactory factory_
;
429 // Tests that when you navigate from a chrome:// url to another page, and
430 // then do that same thing in another tab, that the two resulting pages have
431 // different SiteInstances, BrowsingInstances, and RenderProcessHosts. This is
432 // a regression test for bug 9364.
433 TEST_F(RenderFrameHostManagerTest
, NewTabPageProcesses
) {
434 set_should_create_webui(true);
435 const GURL
kChromeUrl("chrome://foo");
436 const GURL
kDestUrl("http://www.google.com/");
438 // Navigate our first tab to the chrome url and then to the destination,
439 // ensuring we grant bindings to the chrome URL.
440 NavigateActiveAndCommit(kChromeUrl
);
441 EXPECT_TRUE(active_rvh()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
442 NavigateActiveAndCommit(kDestUrl
);
444 EXPECT_FALSE(contents()->GetPendingMainFrame());
446 // Make a second tab.
447 scoped_ptr
<TestWebContents
> contents2(
448 TestWebContents::Create(browser_context(), NULL
));
450 // Load the two URLs in the second tab. Note that the first navigation creates
451 // a RFH that's not pending (since there is no cross-site transition), so
452 // we use the committed one.
453 contents2
->GetController().LoadURL(
454 kChromeUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
455 int entry_id
= contents2
->GetController().GetPendingEntry()->GetUniqueID();
456 contents2
->GetMainFrame()->PrepareForCommit();
457 TestRenderFrameHost
* ntp_rfh2
= contents2
->GetMainFrame();
458 EXPECT_FALSE(contents2
->CrossProcessNavigationPending());
459 ntp_rfh2
->SendNavigate(100, entry_id
, true, kChromeUrl
);
461 // The second one is the opposite, creating a cross-site transition and
462 // requiring a beforeunload ack.
463 contents2
->GetController().LoadURL(
464 kDestUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
465 entry_id
= contents2
->GetController().GetPendingEntry()->GetUniqueID();
466 contents2
->GetMainFrame()->PrepareForCommit();
467 EXPECT_TRUE(contents2
->CrossProcessNavigationPending());
468 TestRenderFrameHost
* dest_rfh2
= contents2
->GetPendingMainFrame();
469 ASSERT_TRUE(dest_rfh2
);
471 dest_rfh2
->SendNavigate(101, entry_id
, true, kDestUrl
);
473 // The two RFH's should be different in every way.
474 EXPECT_NE(contents()->GetMainFrame()->GetProcess(), dest_rfh2
->GetProcess());
475 EXPECT_NE(contents()->GetMainFrame()->GetSiteInstance(),
476 dest_rfh2
->GetSiteInstance());
477 EXPECT_FALSE(dest_rfh2
->GetSiteInstance()->IsRelatedSiteInstance(
478 contents()->GetMainFrame()->GetSiteInstance()));
480 // Navigate both to the new tab page, and verify that they share a
481 // RenderProcessHost (not a SiteInstance).
482 NavigateActiveAndCommit(kChromeUrl
);
483 EXPECT_FALSE(contents()->GetPendingMainFrame());
485 contents2
->GetController().LoadURL(
486 kChromeUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
487 entry_id
= contents2
->GetController().GetPendingEntry()->GetUniqueID();
488 contents2
->GetMainFrame()->PrepareForCommit();
489 contents2
->GetPendingMainFrame()->SendNavigate(102, entry_id
, true,
492 EXPECT_NE(contents()->GetMainFrame()->GetSiteInstance(),
493 contents2
->GetMainFrame()->GetSiteInstance());
494 EXPECT_EQ(contents()->GetMainFrame()->GetSiteInstance()->GetProcess(),
495 contents2
->GetMainFrame()->GetSiteInstance()->GetProcess());
498 // Ensure that the browser ignores most IPC messages that arrive from a
499 // RenderViewHost that has been swapped out. We do not want to take
500 // action on requests from a non-active renderer. The main exception is
501 // for synchronous messages, which cannot be ignored without leaving the
502 // renderer in a stuck state. See http://crbug.com/93427.
503 TEST_F(RenderFrameHostManagerTest
, FilterMessagesWhileSwappedOut
) {
504 const GURL
kChromeURL("chrome://foo");
505 const GURL
kDestUrl("http://www.google.com/");
506 std::vector
<FaviconURL
> icons
;
508 // Navigate our first tab to a chrome url and then to the destination.
509 NavigateActiveAndCommit(kChromeURL
);
510 TestRenderFrameHost
* ntp_rfh
= contents()->GetMainFrame();
511 TestRenderViewHost
* ntp_rvh
= ntp_rfh
->GetRenderViewHost();
513 // Send an update favicon message and make sure it works.
515 PluginFaviconMessageObserver
observer(contents());
516 EXPECT_TRUE(ntp_rfh
->GetRenderViewHost()->OnMessageReceived(
517 ViewHostMsg_UpdateFaviconURL(
518 ntp_rfh
->GetRenderViewHost()->GetRoutingID(), icons
)));
519 EXPECT_TRUE(observer
.favicon_received());
521 // Create one more frame in the same SiteInstance where ntp_rfh
522 // exists so that it doesn't get deleted on navigation to another
524 ntp_rfh
->GetSiteInstance()->increment_active_frame_count();
526 // Navigate to a cross-site URL.
527 NavigateActiveAndCommit(kDestUrl
);
528 TestRenderFrameHost
* dest_rfh
= contents()->GetMainFrame();
529 ASSERT_TRUE(dest_rfh
);
530 EXPECT_NE(ntp_rfh
, dest_rfh
);
532 // The new RVH should be able to update its favicon.
534 PluginFaviconMessageObserver
observer(contents());
536 dest_rfh
->GetRenderViewHost()->OnMessageReceived(
537 ViewHostMsg_UpdateFaviconURL(
538 dest_rfh
->GetRenderViewHost()->GetRoutingID(), icons
)));
539 EXPECT_TRUE(observer
.favicon_received());
542 // The old renderer, being slow, now updates the favicon. It should be
543 // filtered out and not take effect.
545 PluginFaviconMessageObserver
observer(contents());
547 ntp_rvh
->OnMessageReceived(
548 ViewHostMsg_UpdateFaviconURL(
549 dest_rfh
->GetRenderViewHost()->GetRoutingID(), icons
)));
550 EXPECT_FALSE(observer
.favicon_received());
553 // In --site-per-process, the RenderFrameHost is deleted on cross-process
554 // navigation, so the rest of the test case doesn't apply.
555 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
559 #if defined(ENABLE_PLUGINS)
560 // The same logic should apply to RenderFrameHosts as well and routing through
561 // swapped out RFH shouldn't be allowed. Use a PluginCrashObserver to check
562 // if the IPC message is allowed through or not.
564 PluginFaviconMessageObserver
observer(contents());
565 EXPECT_TRUE(ntp_rfh
->OnMessageReceived(
566 FrameHostMsg_PluginCrashed(
567 ntp_rfh
->GetRoutingID(), base::FilePath(), 0)));
568 EXPECT_FALSE(observer
.plugin_crashed());
572 // We cannot filter out synchronous IPC messages, because the renderer would
573 // be left waiting for a reply. We pick RunBeforeUnloadConfirm as an example
574 // that can run easily within a unit test, and that needs to receive a reply
575 // without showing an actual dialog.
576 MockRenderProcessHost
* ntp_process_host
= ntp_rfh
->GetProcess();
577 ntp_process_host
->sink().ClearMessages();
578 const base::string16 msg
= base::ASCIIToUTF16("Message");
580 base::string16 unused
;
581 FrameHostMsg_RunBeforeUnloadConfirm
before_unload_msg(
582 ntp_rfh
->GetRoutingID(), kChromeURL
, msg
, false, &result
, &unused
);
583 // Enable pumping for check in BrowserMessageFilter::CheckCanDispatchOnUI.
584 before_unload_msg
.EnableMessagePumping();
585 EXPECT_TRUE(ntp_rfh
->OnMessageReceived(before_unload_msg
));
586 EXPECT_TRUE(ntp_process_host
->sink().GetUniqueMessageMatching(IPC_REPLY_ID
));
588 // Also test RunJavaScriptMessage.
589 ntp_process_host
->sink().ClearMessages();
590 FrameHostMsg_RunJavaScriptMessage
js_msg(
591 ntp_rfh
->GetRoutingID(), msg
, msg
, kChromeURL
,
592 JAVASCRIPT_MESSAGE_TYPE_CONFIRM
, &result
, &unused
);
593 js_msg
.EnableMessagePumping();
594 EXPECT_TRUE(ntp_rfh
->OnMessageReceived(js_msg
));
595 EXPECT_TRUE(ntp_process_host
->sink().GetUniqueMessageMatching(IPC_REPLY_ID
));
598 // Test that the ViewHostMsg_UpdateFaviconURL IPC message is ignored if the
599 // renderer is in the STATE_PENDING_SWAP_OUT_STATE. The favicon code assumes
600 // that it only gets ViewHostMsg_UpdateFaviconURL messages for the most recently
601 // committed navigation for each WebContentsImpl.
602 TEST_F(RenderFrameHostManagerTest
, UpdateFaviconURLWhilePendingSwapOut
) {
603 const GURL
kChromeURL("chrome://foo");
604 const GURL
kDestUrl("http://www.google.com/");
605 std::vector
<FaviconURL
> icons
;
607 // Navigate our first tab to a chrome url and then to the destination.
608 NavigateActiveAndCommit(kChromeURL
);
609 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
611 // Send an update favicon message and make sure it works.
613 PluginFaviconMessageObserver
observer(contents());
614 EXPECT_TRUE(rfh1
->GetRenderViewHost()->OnMessageReceived(
615 ViewHostMsg_UpdateFaviconURL(
616 rfh1
->GetRenderViewHost()->GetRoutingID(), icons
)));
617 EXPECT_TRUE(observer
.favicon_received());
620 // Create one more frame in the same SiteInstance where |rfh1| exists so that
621 // it doesn't get deleted on navigation to another site.
622 rfh1
->GetSiteInstance()->increment_active_frame_count();
624 // Navigate to a cross-site URL and commit the new page.
625 controller().LoadURL(
626 kDestUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
627 int entry_id
= controller().GetPendingEntry()->GetUniqueID();
628 contents()->GetMainFrame()->PrepareForCommit();
629 TestRenderFrameHost
* rfh2
= contents()->GetPendingMainFrame();
630 contents()->TestDidNavigate(rfh2
, 1, entry_id
, true, kDestUrl
,
631 ui::PAGE_TRANSITION_TYPED
);
632 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
633 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
635 // The new RVH should be able to update its favicons.
637 PluginFaviconMessageObserver
observer(contents());
638 EXPECT_TRUE(rfh2
->GetRenderViewHost()->OnMessageReceived(
639 ViewHostMsg_UpdateFaviconURL(rfh2
->GetRenderViewHost()->GetRoutingID(),
641 EXPECT_TRUE(observer
.favicon_received());
644 // The old renderer, being slow, now updates its favicons. The message should
647 PluginFaviconMessageObserver
observer(contents());
648 EXPECT_TRUE(rfh1
->GetRenderViewHost()->OnMessageReceived(
649 ViewHostMsg_UpdateFaviconURL(rfh1
->GetRenderViewHost()->GetRoutingID(),
651 EXPECT_FALSE(observer
.favicon_received());
655 // Ensure that frames aren't added to the frame tree, if the message is coming
656 // from a process different than the parent frame's current RenderFrameHost
657 // process. Otherwise it is possible to have collisions of routing ids, as they
658 // are scoped per process. See https://crbug.com/415059.
659 TEST_F(RenderFrameHostManagerTest
, DropCreateChildFrameWhileSwappedOut
) {
660 const GURL
kUrl1("http://foo.com");
661 const GURL
kUrl2("http://www.google.com/");
663 // This test is invalid in --site-per-process mode, as swapped-out is no
665 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
669 // Navigate to the first site.
670 NavigateActiveAndCommit(kUrl1
);
671 TestRenderFrameHost
* initial_rfh
= contents()->GetMainFrame();
673 RenderFrameHostCreatedObserver
observer(contents());
674 initial_rfh
->OnCreateChildFrame(
675 initial_rfh
->GetProcess()->GetNextRoutingID(),
676 blink::WebTreeScopeType::Document
, std::string(),
677 blink::WebSandboxFlags::None
);
678 EXPECT_TRUE(observer
.created());
681 // Create one more frame in the same SiteInstance where initial_rfh
682 // exists so that initial_rfh doesn't get deleted on navigation to another
684 initial_rfh
->GetSiteInstance()->increment_active_frame_count();
686 // Navigate to a cross-site URL.
687 NavigateActiveAndCommit(kUrl2
);
688 EXPECT_TRUE(initial_rfh
->is_swapped_out());
690 TestRenderFrameHost
* dest_rfh
= contents()->GetMainFrame();
691 ASSERT_TRUE(dest_rfh
);
692 EXPECT_NE(initial_rfh
, dest_rfh
);
695 // Since the old RFH is now swapped out, it shouldn't process any messages
696 // to create child frames.
697 RenderFrameHostCreatedObserver
observer(contents());
698 initial_rfh
->OnCreateChildFrame(
699 initial_rfh
->GetProcess()->GetNextRoutingID(),
700 blink::WebTreeScopeType::Document
, std::string(),
701 blink::WebSandboxFlags::None
);
702 EXPECT_FALSE(observer
.created());
706 TEST_F(RenderFrameHostManagerTest
, WhiteListSwapCompositorFrame
) {
707 // TODO(nasko): Check with kenrb whether this test can be rewritten and
708 // whether it makes sense when swapped out is replaced with proxies.
709 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
712 TestRenderFrameHost
* swapped_out_rfh
= CreateSwappedOutRenderFrameHost();
713 TestRenderWidgetHostView
* swapped_out_rwhv
=
714 static_cast<TestRenderWidgetHostView
*>(
715 swapped_out_rfh
->GetRenderViewHost()->GetView());
716 EXPECT_FALSE(swapped_out_rwhv
->did_swap_compositor_frame());
718 MockRenderProcessHost
* process_host
= swapped_out_rfh
->GetProcess();
719 process_host
->sink().ClearMessages();
721 cc::CompositorFrame frame
;
722 ViewHostMsg_SwapCompositorFrame
msg(
723 rvh()->GetRoutingID(), 0, frame
, std::vector
<IPC::Message
>());
725 EXPECT_TRUE(swapped_out_rfh
->render_view_host()->OnMessageReceived(msg
));
726 EXPECT_TRUE(swapped_out_rwhv
->did_swap_compositor_frame());
729 // Test if RenderViewHost::GetRenderWidgetHosts() only returns active
731 TEST_F(RenderFrameHostManagerTest
, GetRenderWidgetHostsReturnsActiveViews
) {
732 // This test is invalid in --site-per-process mode, as swapped-out is no
734 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
738 TestRenderFrameHost
* swapped_out_rfh
= CreateSwappedOutRenderFrameHost();
739 EXPECT_TRUE(swapped_out_rfh
->is_swapped_out());
741 scoped_ptr
<RenderWidgetHostIterator
> widgets(
742 RenderWidgetHost::GetRenderWidgetHosts());
743 // We know that there is the only one active widget. Another view is
744 // now swapped out, so the swapped out view is not included in the
746 RenderWidgetHost
* widget
= widgets
->GetNextHost();
747 EXPECT_FALSE(widgets
->GetNextHost());
748 RenderViewHost
* rvh
= RenderViewHost::From(widget
);
749 EXPECT_TRUE(static_cast<RenderViewHostImpl
*>(rvh
)->is_active());
752 // Test if RenderViewHost::GetRenderWidgetHosts() returns a subset of
753 // RenderViewHostImpl::GetAllRenderWidgetHosts().
754 // RenderViewHost::GetRenderWidgetHosts() returns only active widgets, but
755 // RenderViewHostImpl::GetAllRenderWidgetHosts() returns everything
756 // including swapped out ones.
757 TEST_F(RenderFrameHostManagerTest
,
758 GetRenderWidgetHostsWithinGetAllRenderWidgetHosts
) {
759 // This test is invalid in --site-per-process mode, as swapped-out is no
761 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
765 TestRenderFrameHost
* swapped_out_rfh
= CreateSwappedOutRenderFrameHost();
766 EXPECT_TRUE(swapped_out_rfh
->is_swapped_out());
768 scoped_ptr
<RenderWidgetHostIterator
> widgets(
769 RenderWidgetHost::GetRenderWidgetHosts());
771 while (RenderWidgetHost
* w
= widgets
->GetNextHost()) {
773 scoped_ptr
<RenderWidgetHostIterator
> all_widgets(
774 RenderWidgetHostImpl::GetAllRenderWidgetHosts());
775 while (RenderWidgetHost
* widget
= all_widgets
->GetNextHost()) {
785 // Test if SiteInstanceImpl::active_frame_count() is correctly updated
786 // as frames in a SiteInstance get swapped out and in.
787 TEST_F(RenderFrameHostManagerTest
, ActiveFrameCountWhileSwappingInAndOut
) {
788 const GURL
kUrl1("http://www.google.com/");
789 const GURL
kUrl2("http://www.chromium.org/");
791 // Navigate to an initial URL.
792 contents()->NavigateAndCommit(kUrl1
);
793 TestRenderFrameHost
* rfh1
= main_test_rfh();
795 SiteInstanceImpl
* instance1
= rfh1
->GetSiteInstance();
796 EXPECT_EQ(instance1
->active_frame_count(), 1U);
798 // Create 2 new tabs and simulate them being the opener chain for the main
799 // tab. They should be in the same SiteInstance.
800 scoped_ptr
<TestWebContents
> opener1(
801 TestWebContents::Create(browser_context(), instance1
));
802 contents()->SetOpener(opener1
.get());
804 scoped_ptr
<TestWebContents
> opener2(
805 TestWebContents::Create(browser_context(), instance1
));
806 opener1
->SetOpener(opener2
.get());
808 EXPECT_EQ(instance1
->active_frame_count(), 3U);
810 // Navigate to a cross-site URL (different SiteInstance but same
811 // BrowsingInstance).
812 contents()->NavigateAndCommit(kUrl2
);
813 TestRenderFrameHost
* rfh2
= main_test_rfh();
814 SiteInstanceImpl
* instance2
= rfh2
->GetSiteInstance();
816 // rvh2 is on chromium.org which is different from google.com on
817 // which other tabs are.
818 EXPECT_EQ(instance2
->active_frame_count(), 1U);
820 // There are two active views on google.com now.
821 EXPECT_EQ(instance1
->active_frame_count(), 2U);
823 // Navigate to the original origin (google.com).
824 contents()->NavigateAndCommit(kUrl1
);
826 EXPECT_EQ(instance1
->active_frame_count(), 3U);
829 // This deletes a WebContents when the given RVH is deleted. This is
830 // only for testing whether deleting an RVH does not cause any UaF in
831 // other parts of the system. For now, this class is only used for the
832 // next test cases to detect the bug mentioned at
833 // http://crbug.com/259859.
834 class RenderViewHostDestroyer
: public WebContentsObserver
{
836 RenderViewHostDestroyer(RenderViewHost
* render_view_host
,
837 WebContents
* web_contents
)
838 : WebContentsObserver(WebContents::FromRenderViewHost(render_view_host
)),
839 render_view_host_(render_view_host
),
840 web_contents_(web_contents
) {}
842 void RenderViewDeleted(RenderViewHost
* render_view_host
) override
{
843 if (render_view_host
== render_view_host_
)
844 delete web_contents_
;
848 RenderViewHost
* render_view_host_
;
849 WebContents
* web_contents_
;
851 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestroyer
);
854 // Test if ShutdownRenderViewHostsInSiteInstance() does not touch any
855 // RenderWidget that has been freed while deleting a RenderViewHost in
856 // a previous iteration. This is a regression test for
857 // http://crbug.com/259859.
858 TEST_F(RenderFrameHostManagerTest
,
859 DetectUseAfterFreeInShutdownRenderViewHostsInSiteInstance
) {
860 const GURL
kChromeURL("chrome://newtab");
861 const GURL
kUrl1("http://www.google.com");
862 const GURL
kUrl2("http://www.chromium.org");
864 // Navigate our first tab to a chrome url and then to the destination.
865 NavigateActiveAndCommit(kChromeURL
);
866 TestRenderFrameHost
* ntp_rfh
= contents()->GetMainFrame();
868 // Create one more tab and navigate to kUrl1. web_contents is not
869 // wrapped as scoped_ptr since it intentionally deleted by destroyer
870 // below as part of this test.
871 TestWebContents
* web_contents
=
872 TestWebContents::Create(browser_context(), ntp_rfh
->GetSiteInstance());
873 web_contents
->NavigateAndCommit(kUrl1
);
874 RenderViewHostDestroyer
destroyer(ntp_rfh
->GetRenderViewHost(),
877 // This causes the first tab to navigate to kUrl2, which destroys
878 // the ntp_rfh in ShutdownRenderViewHostsInSiteInstance(). When
879 // ntp_rfh is destroyed, it also destroys the RVHs in web_contents
880 // too. This can test whether
881 // SiteInstanceImpl::ShutdownRenderViewHostsInSiteInstance() can
882 // touch any object freed in this way or not while iterating through
884 contents()->NavigateAndCommit(kUrl2
);
887 // When there is an error with the specified page, renderer exits view-source
888 // mode. See WebFrameImpl::DidFail(). We check by this test that
889 // EnableViewSourceMode message is sent on every navigation regardless
890 // RenderView is being newly created or reused.
891 TEST_F(RenderFrameHostManagerTest
, AlwaysSendEnableViewSourceMode
) {
892 const GURL
kChromeUrl("chrome://foo/");
893 const GURL
kUrl("http://foo/");
894 const GURL
kViewSourceUrl("view-source:http://foo/");
896 // We have to navigate to some page at first since without this, the first
897 // navigation will reuse the SiteInstance created by Init(), and the second
898 // one will create a new SiteInstance. Because current_instance and
899 // new_instance will be different, a new RenderViewHost will be created for
900 // the second navigation. We have to avoid this in order to exercise the
902 NavigateActiveAndCommit(kChromeUrl
);
904 // Navigate. Note that "view source" URLs are implemented by putting the RFH
905 // into a view-source mode and then navigating to the inner URL, so that's why
906 // the bare URL is what's committed and returned by the last committed entry's
908 controller().LoadURL(
909 kViewSourceUrl
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
910 int entry_id
= controller().GetPendingEntry()->GetUniqueID();
912 // Simulate response from RenderFrame for DispatchBeforeUnload.
913 contents()->GetMainFrame()->PrepareForCommit();
914 ASSERT_TRUE(contents()->GetPendingMainFrame())
915 << "Expected new pending RenderFrameHost to be created.";
916 RenderFrameHost
* last_rfh
= contents()->GetPendingMainFrame();
918 contents()->GetMaxPageIDForSiteInstance(last_rfh
->GetSiteInstance()) + 1;
919 contents()->GetPendingMainFrame()->SendNavigate(new_id
, entry_id
, true, kUrl
);
921 EXPECT_EQ(1, controller().GetLastCommittedEntryIndex());
922 NavigationEntry
* last_committed
= controller().GetLastCommittedEntry();
923 ASSERT_NE(nullptr, last_committed
);
924 EXPECT_EQ(kUrl
, last_committed
->GetURL());
925 EXPECT_EQ(kViewSourceUrl
, last_committed
->GetVirtualURL());
926 EXPECT_FALSE(controller().GetPendingEntry());
927 // Because we're using TestWebContents and TestRenderViewHost in this
928 // unittest, no one calls WebContentsImpl::RenderViewCreated(). So, we see no
929 // EnableViewSourceMode message, here.
931 // Clear queued messages before load.
932 process()->sink().ClearMessages();
935 controller().LoadURL(
936 kViewSourceUrl
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
937 entry_id
= controller().GetPendingEntry()->GetUniqueID();
938 contents()->GetMainFrame()->PrepareForCommit();
940 // The same RenderViewHost should be reused.
941 EXPECT_FALSE(contents()->GetPendingMainFrame());
942 EXPECT_EQ(last_rfh
, contents()->GetMainFrame());
944 // The renderer sends a commit.
945 contents()->GetMainFrame()->SendNavigateWithTransition(
946 new_id
, entry_id
, false, kUrl
, ui::PAGE_TRANSITION_TYPED
);
947 EXPECT_EQ(1, controller().GetLastCommittedEntryIndex());
948 EXPECT_FALSE(controller().GetPendingEntry());
950 // New message should be sent out to make sure to enter view-source mode.
951 EXPECT_TRUE(process()->sink().GetUniqueMessageMatching(
952 ViewMsg_EnableViewSourceMode::ID
));
955 // Tests the Init function by checking the initial RenderViewHost.
956 TEST_F(RenderFrameHostManagerTest
, Init
) {
957 // Using TestBrowserContext.
958 SiteInstanceImpl
* instance
=
959 static_cast<SiteInstanceImpl
*>(SiteInstance::Create(browser_context()));
960 EXPECT_FALSE(instance
->HasSite());
962 scoped_ptr
<TestWebContents
> web_contents(
963 TestWebContents::Create(browser_context(), instance
));
965 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
966 RenderViewHostImpl
* rvh
= manager
->current_host();
967 RenderFrameHostImpl
* rfh
= manager
->current_frame_host();
970 EXPECT_EQ(rvh
, rfh
->render_view_host());
971 EXPECT_EQ(instance
, rvh
->GetSiteInstance());
972 EXPECT_EQ(web_contents
.get(), rvh
->GetDelegate());
973 EXPECT_EQ(web_contents
.get(), rfh
->delegate());
974 EXPECT_TRUE(manager
->GetRenderWidgetHostView());
975 EXPECT_FALSE(manager
->pending_render_view_host());
978 // Tests the Navigate function. We navigate three sites consecutively and check
979 // how the pending/committed RenderViewHost are modified.
980 TEST_F(RenderFrameHostManagerTest
, Navigate
) {
981 SiteInstance
* instance
= SiteInstance::Create(browser_context());
983 scoped_ptr
<TestWebContents
> web_contents(
984 TestWebContents::Create(browser_context(), instance
));
985 RenderViewHostChangedObserver
change_observer(web_contents
.get());
987 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
988 RenderFrameHostImpl
* host
= NULL
;
990 // 1) The first navigation. --------------------------
991 const GURL
kUrl1("http://www.google.com/");
992 NavigationEntryImpl
entry1(
993 NULL
/* instance */, -1 /* page_id */, kUrl1
, Referrer(),
994 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED
,
995 false /* is_renderer_init */);
996 host
= NavigateToEntry(manager
, entry1
);
998 // The RenderFrameHost created in Init will be reused.
999 EXPECT_TRUE(host
== manager
->current_frame_host());
1000 EXPECT_FALSE(GetPendingFrameHost(manager
));
1003 manager
->DidNavigateFrame(host
, true);
1004 // Commit to SiteInstance should be delayed until RenderFrame commit.
1005 EXPECT_TRUE(host
== manager
->current_frame_host());
1007 EXPECT_FALSE(host
->GetSiteInstance()->HasSite());
1008 host
->GetSiteInstance()->SetSite(kUrl1
);
1010 // 2) Navigate to next site. -------------------------
1011 const GURL
kUrl2("http://www.google.com/foo");
1012 NavigationEntryImpl
entry2(
1013 NULL
/* instance */, -1 /* page_id */, kUrl2
,
1014 Referrer(kUrl1
, blink::WebReferrerPolicyDefault
),
1015 base::string16() /* title */, ui::PAGE_TRANSITION_LINK
,
1016 true /* is_renderer_init */);
1017 host
= NavigateToEntry(manager
, entry2
);
1019 // The RenderFrameHost created in Init will be reused.
1020 EXPECT_TRUE(host
== manager
->current_frame_host());
1021 EXPECT_FALSE(GetPendingFrameHost(manager
));
1024 manager
->DidNavigateFrame(host
, true);
1025 EXPECT_TRUE(host
== manager
->current_frame_host());
1027 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
1029 // 3) Cross-site navigate to next site. --------------
1030 const GURL
kUrl3("http://webkit.org/");
1031 NavigationEntryImpl
entry3(
1032 NULL
/* instance */, -1 /* page_id */, kUrl3
,
1033 Referrer(kUrl2
, blink::WebReferrerPolicyDefault
),
1034 base::string16() /* title */, ui::PAGE_TRANSITION_LINK
,
1035 false /* is_renderer_init */);
1036 host
= NavigateToEntry(manager
, entry3
);
1038 // A new RenderFrameHost should be created.
1039 EXPECT_TRUE(GetPendingFrameHost(manager
));
1040 ASSERT_EQ(host
, GetPendingFrameHost(manager
));
1042 change_observer
.Reset();
1045 manager
->DidNavigateFrame(GetPendingFrameHost(manager
), true);
1046 EXPECT_TRUE(host
== manager
->current_frame_host());
1048 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
1049 // Check the pending RenderFrameHost has been committed.
1050 EXPECT_FALSE(GetPendingFrameHost(manager
));
1052 // We should observe RVH changed event.
1053 EXPECT_TRUE(change_observer
.DidHostChange());
1056 // Tests WebUI creation.
1057 TEST_F(RenderFrameHostManagerTest
, WebUI
) {
1058 set_should_create_webui(true);
1059 SiteInstance
* instance
= SiteInstance::Create(browser_context());
1061 scoped_ptr
<TestWebContents
> web_contents(
1062 TestWebContents::Create(browser_context(), instance
));
1063 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
1064 RenderFrameHostImpl
* initial_rfh
= manager
->current_frame_host();
1066 EXPECT_FALSE(manager
->current_host()->IsRenderViewLive());
1067 EXPECT_FALSE(manager
->web_ui());
1068 EXPECT_TRUE(initial_rfh
);
1070 const GURL
kUrl("chrome://foo");
1071 NavigationEntryImpl
entry(NULL
/* instance */, -1 /* page_id */, kUrl
,
1072 Referrer(), base::string16() /* title */,
1073 ui::PAGE_TRANSITION_TYPED
,
1074 false /* is_renderer_init */);
1075 RenderFrameHostImpl
* host
= NavigateToEntry(manager
, entry
);
1077 // We commit the pending RenderFrameHost immediately because the previous
1078 // RenderFrameHost was not live. We test a case where it is live in
1081 EXPECT_NE(initial_rfh
, host
);
1082 EXPECT_EQ(host
, manager
->current_frame_host());
1083 EXPECT_FALSE(GetPendingFrameHost(manager
));
1085 // It's important that the SiteInstance get set on the Web UI page as soon
1086 // as the navigation starts, rather than lazily after it commits, so we don't
1087 // try to re-use the SiteInstance/process for non Web UI things that may
1088 // get loaded in between.
1089 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
1090 EXPECT_EQ(kUrl
, host
->GetSiteInstance()->GetSiteURL());
1092 // The Web UI is committed immediately because the RenderViewHost has not been
1093 // used yet. UpdateStateForNavigate() took the short cut path.
1094 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1095 switches::kEnableBrowserSideNavigation
)) {
1096 EXPECT_FALSE(manager
->speculative_web_ui());
1098 EXPECT_FALSE(manager
->pending_web_ui());
1100 EXPECT_TRUE(manager
->web_ui());
1103 manager
->DidNavigateFrame(host
, true);
1105 host
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1108 // Tests that we can open a WebUI link in a new tab from a WebUI page and still
1109 // grant the correct bindings. http://crbug.com/189101.
1110 TEST_F(RenderFrameHostManagerTest
, WebUIInNewTab
) {
1111 set_should_create_webui(true);
1112 SiteInstance
* blank_instance
= SiteInstance::Create(browser_context());
1113 blank_instance
->GetProcess()->Init();
1115 // Create a blank tab.
1116 scoped_ptr
<TestWebContents
> web_contents1(
1117 TestWebContents::Create(browser_context(), blank_instance
));
1118 RenderFrameHostManager
* manager1
=
1119 web_contents1
->GetRenderManagerForTesting();
1120 // Test the case that new RVH is considered live.
1121 manager1
->current_host()->CreateRenderView(-1, MSG_ROUTING_NONE
, -1,
1122 FrameReplicationState(), false);
1123 EXPECT_TRUE(manager1
->current_host()->IsRenderViewLive());
1124 EXPECT_TRUE(manager1
->current_frame_host()->IsRenderFrameLive());
1126 // Navigate to a WebUI page.
1127 const GURL
kUrl1("chrome://foo");
1128 NavigationEntryImpl
entry1(NULL
/* instance */, -1 /* page_id */, kUrl1
,
1129 Referrer(), base::string16() /* title */,
1130 ui::PAGE_TRANSITION_TYPED
,
1131 false /* is_renderer_init */);
1132 RenderFrameHostImpl
* host1
= NavigateToEntry(manager1
, entry1
);
1134 // We should have a pending navigation to the WebUI RenderViewHost.
1135 // It should already have bindings.
1136 EXPECT_EQ(host1
, GetPendingFrameHost(manager1
));
1137 EXPECT_NE(host1
, manager1
->current_frame_host());
1139 host1
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1141 // Commit and ensure we still have bindings.
1142 manager1
->DidNavigateFrame(host1
, true);
1143 SiteInstance
* webui_instance
= host1
->GetSiteInstance();
1144 EXPECT_EQ(host1
, manager1
->current_frame_host());
1146 host1
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1148 // Now simulate clicking a link that opens in a new tab.
1149 scoped_ptr
<TestWebContents
> web_contents2(
1150 TestWebContents::Create(browser_context(), webui_instance
));
1151 RenderFrameHostManager
* manager2
=
1152 web_contents2
->GetRenderManagerForTesting();
1153 // Make sure the new RVH is considered live. This is usually done in
1154 // RenderWidgetHost::Init when opening a new tab from a link.
1155 manager2
->current_host()->CreateRenderView(-1, MSG_ROUTING_NONE
, -1,
1156 FrameReplicationState(), false);
1157 EXPECT_TRUE(manager2
->current_host()->IsRenderViewLive());
1159 const GURL
kUrl2("chrome://foo/bar");
1160 NavigationEntryImpl
entry2(NULL
/* instance */, -1 /* page_id */, kUrl2
,
1161 Referrer(), base::string16() /* title */,
1162 ui::PAGE_TRANSITION_LINK
,
1163 true /* is_renderer_init */);
1164 RenderFrameHostImpl
* host2
= NavigateToEntry(manager2
, entry2
);
1166 // No cross-process transition happens because we are already in the right
1167 // SiteInstance. We should grant bindings immediately.
1168 EXPECT_EQ(host2
, manager2
->current_frame_host());
1169 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1170 switches::kEnableBrowserSideNavigation
)) {
1171 EXPECT_TRUE(manager2
->speculative_web_ui());
1173 EXPECT_TRUE(manager2
->pending_web_ui());
1176 host2
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1178 manager2
->DidNavigateFrame(host2
, true);
1181 // Tests that a WebUI is correctly reused between chrome:// pages.
1182 TEST_F(RenderFrameHostManagerTest
, WebUIWasReused
) {
1183 set_should_create_webui(true);
1185 // Navigate to a WebUI page.
1186 const GURL
kUrl1("chrome://foo");
1187 contents()->NavigateAndCommit(kUrl1
);
1188 RenderFrameHostManager
* manager
=
1189 main_test_rfh()->frame_tree_node()->render_manager();
1190 WebUIImpl
* web_ui
= manager
->web_ui();
1191 EXPECT_TRUE(web_ui
);
1193 // Navigate to another WebUI page which should be same-site and keep the
1195 const GURL
kUrl2("chrome://foo/bar");
1196 contents()->NavigateAndCommit(kUrl2
);
1197 EXPECT_EQ(web_ui
, manager
->web_ui());
1200 // Tests that a WebUI is correctly cleaned up when navigating from a chrome://
1201 // page to a non-chrome:// page.
1202 TEST_F(RenderFrameHostManagerTest
, WebUIWasCleared
) {
1203 set_should_create_webui(true);
1205 // Navigate to a WebUI page.
1206 const GURL
kUrl1("chrome://foo");
1207 contents()->NavigateAndCommit(kUrl1
);
1208 EXPECT_TRUE(main_test_rfh()->frame_tree_node()->render_manager()->web_ui());
1210 // Navigate to a non-WebUI page.
1211 const GURL
kUrl2("http://www.google.com");
1212 contents()->NavigateAndCommit(kUrl2
);
1213 EXPECT_FALSE(main_test_rfh()->frame_tree_node()->render_manager()->web_ui());
1216 // Tests that we don't end up in an inconsistent state if a page does a back and
1217 // then reload. http://crbug.com/51680
1218 // Also tests that only user-gesture navigations can interrupt cross-process
1219 // navigations. http://crbug.com/75195
1220 TEST_F(RenderFrameHostManagerTest
, PageDoesBackAndReload
) {
1221 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1222 switches::kEnableBrowserSideNavigation
)) {
1223 // PlzNavigate uses a significantly different logic for renderer initiated
1224 // navigations and navigation cancellation. Adapting this test would make it
1225 // full of special cases and almost unreadable.
1226 // There are tests that exercise these concerns for PlzNavigate, all from
1227 // NavigatorTestWithBrowserSideNavigation:
1228 // - BrowserInitiatedNavigationCancel
1229 // - RendererUserInitiatedNavigationCancel
1230 // - RendererNonUserInitiatedNavigationDoesntCancelRendererUserInitiated
1231 // - RendererNonUserInitiatedNavigationDoesntCancelBrowserInitiated
1232 // - RendererNonUserInitiatedNavigationCancelSimilarNavigation
1233 SUCCEED() << "Test is not applicable with browser side navigation enabled";
1236 const GURL
kUrl1("http://www.google.com/");
1237 const GURL
kUrl2("http://www.evil-site.com/");
1239 // Navigate to a safe site, then an evil site.
1240 // This will switch RenderFrameHosts. We cannot assert that the first and
1241 // second RFHs are different, though, because the first one may be promptly
1243 contents()->NavigateAndCommit(kUrl1
);
1244 contents()->NavigateAndCommit(kUrl2
);
1245 TestRenderFrameHost
* evil_rfh
= contents()->GetMainFrame();
1247 // Now let's simulate the evil page calling history.back().
1248 contents()->OnGoToEntryAtOffset(-1);
1249 contents()->GetMainFrame()->PrepareForCommit();
1250 // We should have a new pending RFH.
1251 // Note that in this case, the navigation has not committed, so evil_rfh will
1252 // not be deleted yet.
1253 EXPECT_NE(evil_rfh
, contents()->GetPendingMainFrame());
1254 EXPECT_NE(evil_rfh
->GetRenderViewHost(),
1255 contents()->GetPendingMainFrame()->GetRenderViewHost());
1257 // Before that RFH has committed, the evil page reloads itself.
1258 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1260 params
.nav_entry_id
= 0;
1261 params
.did_create_new_entry
= false;
1263 params
.transition
= ui::PAGE_TRANSITION_CLIENT_REDIRECT
;
1264 params
.should_update_history
= false;
1265 params
.gesture
= NavigationGestureAuto
;
1266 params
.was_within_same_page
= false;
1267 params
.is_post
= false;
1268 params
.page_state
= PageState::CreateFromURL(kUrl2
);
1270 evil_rfh
->SimulateNavigationStart(kUrl2
);
1271 evil_rfh
->SendNavigateWithParams(¶ms
);
1272 evil_rfh
->SimulateNavigationStop();
1274 // That should NOT have cancelled the pending RFH, because the reload did
1275 // not have a user gesture. Thus, the pending back navigation will still
1276 // eventually commit.
1277 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
1278 pending_render_view_host() != NULL
);
1279 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->pending_frame_host() !=
1282 contents()->GetRenderManagerForTesting()->current_frame_host());
1283 EXPECT_EQ(evil_rfh
->GetRenderViewHost(),
1284 contents()->GetRenderManagerForTesting()->current_host());
1286 // Also we should not have a pending navigation entry.
1287 EXPECT_TRUE(contents()->GetController().GetPendingEntry() == NULL
);
1288 NavigationEntry
* entry
= contents()->GetController().GetVisibleEntry();
1289 ASSERT_TRUE(entry
!= NULL
);
1290 EXPECT_EQ(kUrl2
, entry
->GetURL());
1292 // Now do the same but as a user gesture.
1293 params
.gesture
= NavigationGestureUser
;
1294 evil_rfh
->SimulateNavigationStart(kUrl2
);
1295 evil_rfh
->SendNavigateWithParams(¶ms
);
1296 evil_rfh
->SimulateNavigationStop();
1298 // User navigation should have cancelled the pending RFH.
1299 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
1300 pending_render_view_host() == NULL
);
1301 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->pending_frame_host() ==
1304 contents()->GetRenderManagerForTesting()->current_frame_host());
1305 EXPECT_EQ(evil_rfh
->GetRenderViewHost(),
1306 contents()->GetRenderManagerForTesting()->current_host());
1308 // Also we should not have a pending navigation entry.
1309 EXPECT_TRUE(contents()->GetController().GetPendingEntry() == NULL
);
1310 entry
= contents()->GetController().GetVisibleEntry();
1311 ASSERT_TRUE(entry
!= NULL
);
1312 EXPECT_EQ(kUrl2
, entry
->GetURL());
1315 // Ensure that we can go back and forward even if a SwapOut ACK isn't received.
1316 // See http://crbug.com/93427.
1317 TEST_F(RenderFrameHostManagerTest
, NavigateAfterMissingSwapOutACK
) {
1318 const GURL
kUrl1("http://www.google.com/");
1319 const GURL
kUrl2("http://www.chromium.org/");
1321 // Navigate to two pages.
1322 contents()->NavigateAndCommit(kUrl1
);
1323 TestRenderFrameHost
* rfh1
= main_test_rfh();
1325 // Keep active_frame_count nonzero so that no swapped out frames in
1326 // this SiteInstance get forcefully deleted.
1327 rfh1
->GetSiteInstance()->increment_active_frame_count();
1329 contents()->NavigateAndCommit(kUrl2
);
1330 TestRenderFrameHost
* rfh2
= main_test_rfh();
1331 rfh2
->GetSiteInstance()->increment_active_frame_count();
1333 // Now go back, but suppose the SwapOut_ACK isn't received. This shouldn't
1334 // happen, but we have seen it when going back quickly across many entries
1335 // (http://crbug.com/93427).
1336 contents()->GetController().GoBack();
1337 EXPECT_TRUE(rfh2
->is_waiting_for_beforeunload_ack());
1338 contents()->GetMainFrame()->PrepareForCommit();
1339 EXPECT_FALSE(rfh2
->is_waiting_for_beforeunload_ack());
1341 // The back navigation commits.
1342 const NavigationEntry
* entry1
= contents()->GetController().GetPendingEntry();
1343 contents()->GetPendingMainFrame()->SendNavigate(
1344 entry1
->GetPageID(), entry1
->GetUniqueID(), false, entry1
->GetURL());
1345 EXPECT_TRUE(rfh2
->IsWaitingForUnloadACK());
1346 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh2
->rfh_state());
1348 // We should be able to navigate forward.
1349 contents()->GetController().GoForward();
1350 contents()->GetMainFrame()->PrepareForCommit();
1351 const NavigationEntry
* entry2
= contents()->GetController().GetPendingEntry();
1352 contents()->GetPendingMainFrame()->SendNavigate(
1353 entry2
->GetPageID(), entry2
->GetUniqueID(), false, entry2
->GetURL());
1354 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, main_test_rfh()->rfh_state());
1355 if (!SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
1356 EXPECT_EQ(rfh2
, main_test_rfh());
1357 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
1358 rfh1
->OnSwappedOut();
1359 EXPECT_TRUE(rfh1
->is_swapped_out());
1360 EXPECT_EQ(RenderFrameHostImpl::STATE_SWAPPED_OUT
, rfh1
->rfh_state());
1364 // Test that we create swapped out RFHs for the opener chain when navigating an
1365 // opened tab cross-process. This allows us to support certain cross-process
1366 // JavaScript calls (http://crbug.com/99202).
1367 TEST_F(RenderFrameHostManagerTest
, CreateSwappedOutOpenerRFHs
) {
1368 const GURL
kUrl1("http://www.google.com/");
1369 const GURL
kUrl2("http://www.chromium.org/");
1370 const GURL
kChromeUrl("chrome://foo");
1372 // Navigate to an initial URL.
1373 contents()->NavigateAndCommit(kUrl1
);
1374 RenderFrameHostManager
* manager
= contents()->GetRenderManagerForTesting();
1375 TestRenderFrameHost
* rfh1
= main_test_rfh();
1376 scoped_refptr
<SiteInstanceImpl
> site_instance1
= rfh1
->GetSiteInstance();
1377 RenderFrameDeletedObserver
rfh1_deleted_observer(rfh1
);
1378 TestRenderViewHost
* rvh1
= test_rvh();
1380 // Create 2 new tabs and simulate them being the opener chain for the main
1381 // tab. They should be in the same SiteInstance.
1382 scoped_ptr
<TestWebContents
> opener1(
1383 TestWebContents::Create(browser_context(), site_instance1
.get()));
1384 RenderFrameHostManager
* opener1_manager
=
1385 opener1
->GetRenderManagerForTesting();
1386 contents()->SetOpener(opener1
.get());
1388 scoped_ptr
<TestWebContents
> opener2(
1389 TestWebContents::Create(browser_context(), site_instance1
.get()));
1390 RenderFrameHostManager
* opener2_manager
=
1391 opener2
->GetRenderManagerForTesting();
1392 opener1
->SetOpener(opener2
.get());
1394 // Navigate to a cross-site URL (different SiteInstance but same
1395 // BrowsingInstance).
1396 contents()->NavigateAndCommit(kUrl2
);
1397 TestRenderFrameHost
* rfh2
= main_test_rfh();
1398 TestRenderViewHost
* rvh2
= test_rvh();
1399 EXPECT_NE(site_instance1
, rfh2
->GetSiteInstance());
1400 EXPECT_TRUE(site_instance1
->IsRelatedSiteInstance(rfh2
->GetSiteInstance()));
1402 // Ensure rvh1 is placed on swapped out list of the current tab.
1403 if (!SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
1404 EXPECT_TRUE(manager
->IsRVHOnSwappedOutList(rvh1
));
1405 EXPECT_FALSE(rfh1_deleted_observer
.deleted());
1406 EXPECT_TRUE(manager
->IsOnSwappedOutList(rfh1
));
1408 manager
->GetRenderFrameProxyHost(site_instance1
.get())
1409 ->render_frame_host());
1411 EXPECT_TRUE(rfh1_deleted_observer
.deleted());
1412 EXPECT_TRUE(manager
->GetRenderFrameProxyHost(site_instance1
.get()));
1415 manager
->GetSwappedOutRenderViewHost(rvh1
->GetSiteInstance()));
1417 // Ensure a swapped out RFH and RFH is created in the first opener tab.
1418 RenderFrameProxyHost
* opener1_proxy
=
1419 opener1_manager
->GetRenderFrameProxyHost(rfh2
->GetSiteInstance());
1420 RenderFrameHostImpl
* opener1_rfh
= opener1_proxy
->render_frame_host();
1421 TestRenderViewHost
* opener1_rvh
= static_cast<TestRenderViewHost
*>(
1422 opener1_manager
->GetSwappedOutRenderViewHost(rvh2
->GetSiteInstance()));
1423 if (!SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
1424 EXPECT_TRUE(opener1_manager
->IsOnSwappedOutList(opener1_rfh
));
1425 EXPECT_TRUE(opener1_manager
->IsRVHOnSwappedOutList(opener1_rvh
));
1426 EXPECT_TRUE(opener1_rfh
->is_swapped_out());
1428 EXPECT_FALSE(opener1_rfh
);
1430 EXPECT_FALSE(opener1_rvh
->is_active());
1432 // Ensure a swapped out RFH and RVH is created in the second opener tab.
1433 RenderFrameProxyHost
* opener2_proxy
=
1434 opener2_manager
->GetRenderFrameProxyHost(rfh2
->GetSiteInstance());
1435 RenderFrameHostImpl
* opener2_rfh
= opener2_proxy
->render_frame_host();
1436 TestRenderViewHost
* opener2_rvh
= static_cast<TestRenderViewHost
*>(
1437 opener2_manager
->GetSwappedOutRenderViewHost(rvh2
->GetSiteInstance()));
1438 if (!SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
1439 EXPECT_TRUE(opener2_manager
->IsOnSwappedOutList(opener2_rfh
));
1440 EXPECT_TRUE(opener2_manager
->IsRVHOnSwappedOutList(opener2_rvh
));
1441 EXPECT_TRUE(opener2_rfh
->is_swapped_out());
1443 EXPECT_FALSE(opener2_rfh
);
1445 EXPECT_FALSE(opener2_rvh
->is_active());
1447 // Navigate to a cross-BrowsingInstance URL.
1448 contents()->NavigateAndCommit(kChromeUrl
);
1449 TestRenderFrameHost
* rfh3
= main_test_rfh();
1450 EXPECT_NE(site_instance1
, rfh3
->GetSiteInstance());
1451 EXPECT_FALSE(site_instance1
->IsRelatedSiteInstance(rfh3
->GetSiteInstance()));
1453 // No scripting is allowed across BrowsingInstances, so we should not create
1454 // swapped out RVHs for the opener chain in this case.
1455 EXPECT_FALSE(opener1_manager
->GetRenderFrameProxyHost(
1456 rfh3
->GetSiteInstance()));
1457 EXPECT_FALSE(opener1_manager
->GetSwappedOutRenderViewHost(
1458 rfh3
->GetSiteInstance()));
1459 EXPECT_FALSE(opener2_manager
->GetRenderFrameProxyHost(
1460 rfh3
->GetSiteInstance()));
1461 EXPECT_FALSE(opener2_manager
->GetSwappedOutRenderViewHost(
1462 rfh3
->GetSiteInstance()));
1465 // Test that a page can disown the opener of the WebContents.
1466 TEST_F(RenderFrameHostManagerTest
, DisownOpener
) {
1467 const GURL
kUrl1("http://www.google.com/");
1468 const GURL
kUrl2("http://www.chromium.org/");
1470 // Navigate to an initial URL.
1471 contents()->NavigateAndCommit(kUrl1
);
1472 TestRenderFrameHost
* rfh1
= main_test_rfh();
1473 scoped_refptr
<SiteInstanceImpl
> site_instance1
= rfh1
->GetSiteInstance();
1475 // Create a new tab and simulate having it be the opener for the main tab.
1476 scoped_ptr
<TestWebContents
> opener1(
1477 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1478 contents()->SetOpener(opener1
.get());
1479 EXPECT_TRUE(contents()->HasOpener());
1481 // Navigate to a cross-site URL (different SiteInstance but same
1482 // BrowsingInstance).
1483 contents()->NavigateAndCommit(kUrl2
);
1484 TestRenderFrameHost
* rfh2
= main_test_rfh();
1485 EXPECT_NE(site_instance1
, rfh2
->GetSiteInstance());
1487 // Disown the opener from rfh2.
1488 rfh2
->DidDisownOpener();
1490 // Ensure the opener is cleared.
1491 EXPECT_FALSE(contents()->HasOpener());
1494 // Test that a page can disown a same-site opener of the WebContents.
1495 TEST_F(RenderFrameHostManagerTest
, DisownSameSiteOpener
) {
1496 const GURL
kUrl1("http://www.google.com/");
1498 // Navigate to an initial URL.
1499 contents()->NavigateAndCommit(kUrl1
);
1500 TestRenderFrameHost
* rfh1
= main_test_rfh();
1502 // Create a new tab and simulate having it be the opener for the main tab.
1503 scoped_ptr
<TestWebContents
> opener1(
1504 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1505 contents()->SetOpener(opener1
.get());
1506 EXPECT_TRUE(contents()->HasOpener());
1508 // Disown the opener from rfh1.
1509 rfh1
->DidDisownOpener();
1511 // Ensure the opener is cleared even if it is in the same process.
1512 EXPECT_FALSE(contents()->HasOpener());
1515 // Test that a page can disown the opener just as a cross-process navigation is
1517 TEST_F(RenderFrameHostManagerTest
, DisownOpenerDuringNavigation
) {
1518 const GURL
kUrl1("http://www.google.com/");
1519 const GURL
kUrl2("http://www.chromium.org/");
1521 // Navigate to an initial URL.
1522 contents()->NavigateAndCommit(kUrl1
);
1523 scoped_refptr
<SiteInstanceImpl
> site_instance1
=
1524 main_test_rfh()->GetSiteInstance();
1526 // Create a new tab and simulate having it be the opener for the main tab.
1527 scoped_ptr
<TestWebContents
> opener1(
1528 TestWebContents::Create(browser_context(), site_instance1
.get()));
1529 contents()->SetOpener(opener1
.get());
1530 EXPECT_TRUE(contents()->HasOpener());
1532 // Navigate to a cross-site URL (different SiteInstance but same
1533 // BrowsingInstance).
1534 contents()->NavigateAndCommit(kUrl2
);
1535 TestRenderFrameHost
* rfh2
= main_test_rfh();
1536 EXPECT_NE(site_instance1
, rfh2
->GetSiteInstance());
1538 // Start a back navigation.
1539 contents()->GetController().GoBack();
1540 contents()->GetMainFrame()->PrepareForCommit();
1542 // Disown the opener from rfh2.
1543 rfh2
->DidDisownOpener();
1545 // Ensure the opener is cleared.
1546 EXPECT_FALSE(contents()->HasOpener());
1548 // The back navigation commits.
1549 const NavigationEntry
* entry1
= contents()->GetController().GetPendingEntry();
1550 contents()->GetPendingMainFrame()->SendNavigate(
1551 entry1
->GetPageID(), entry1
->GetUniqueID(), false, entry1
->GetURL());
1553 // Ensure the opener is still cleared.
1554 EXPECT_FALSE(contents()->HasOpener());
1557 // Test that a page can disown the opener just after a cross-process navigation
1559 TEST_F(RenderFrameHostManagerTest
, DisownOpenerAfterNavigation
) {
1560 const GURL
kUrl1("http://www.google.com/");
1561 const GURL
kUrl2("http://www.chromium.org/");
1563 // Navigate to an initial URL.
1564 contents()->NavigateAndCommit(kUrl1
);
1565 scoped_refptr
<SiteInstanceImpl
> site_instance1
=
1566 main_test_rfh()->GetSiteInstance();
1568 // Create a new tab and simulate having it be the opener for the main tab.
1569 scoped_ptr
<TestWebContents
> opener1(
1570 TestWebContents::Create(browser_context(), site_instance1
.get()));
1571 contents()->SetOpener(opener1
.get());
1572 EXPECT_TRUE(contents()->HasOpener());
1574 // Navigate to a cross-site URL (different SiteInstance but same
1575 // BrowsingInstance).
1576 contents()->NavigateAndCommit(kUrl2
);
1577 TestRenderFrameHost
* rfh2
= main_test_rfh();
1578 EXPECT_NE(site_instance1
, rfh2
->GetSiteInstance());
1580 // Commit a back navigation before the DidDisownOpener message arrives.
1581 contents()->GetController().GoBack();
1582 contents()->GetMainFrame()->PrepareForCommit();
1583 const NavigationEntry
* entry1
= contents()->GetController().GetPendingEntry();
1584 contents()->GetPendingMainFrame()->SendNavigate(
1585 entry1
->GetPageID(), entry1
->GetUniqueID(), false, entry1
->GetURL());
1587 // Disown the opener from rfh2.
1588 rfh2
->DidDisownOpener();
1589 EXPECT_FALSE(contents()->HasOpener());
1592 // Test that we clean up swapped out RenderViewHosts when a process hosting
1593 // those associated RenderViews crashes. http://crbug.com/258993
1594 TEST_F(RenderFrameHostManagerTest
, CleanUpSwappedOutRVHOnProcessCrash
) {
1595 const GURL
kUrl1("http://www.google.com/");
1596 const GURL
kUrl2("http://www.chromium.org/");
1598 // Navigate to an initial URL.
1599 contents()->NavigateAndCommit(kUrl1
);
1600 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1602 // Create a new tab as an opener for the main tab.
1603 scoped_ptr
<TestWebContents
> opener1(
1604 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1605 RenderFrameHostManager
* opener1_manager
=
1606 opener1
->GetRenderManagerForTesting();
1607 contents()->SetOpener(opener1
.get());
1609 // Make sure the new opener RVH is considered live.
1610 opener1_manager
->current_host()->CreateRenderView(
1611 -1, MSG_ROUTING_NONE
, -1, FrameReplicationState(), false);
1612 EXPECT_TRUE(opener1_manager
->current_host()->IsRenderViewLive());
1613 EXPECT_TRUE(opener1_manager
->current_frame_host()->IsRenderFrameLive());
1615 // Use a cross-process navigation in the opener to swap out the old RVH.
1617 opener1_manager
->GetSwappedOutRenderViewHost(rfh1
->GetSiteInstance()));
1618 opener1
->NavigateAndCommit(kUrl2
);
1619 RenderViewHostImpl
* swapped_out_rvh
=
1620 opener1_manager
->GetSwappedOutRenderViewHost(rfh1
->GetSiteInstance());
1621 EXPECT_TRUE(swapped_out_rvh
);
1622 EXPECT_TRUE(swapped_out_rvh
->is_swapped_out_
);
1623 EXPECT_FALSE(swapped_out_rvh
->is_active());
1625 // Fake a process crash.
1626 rfh1
->GetProcess()->SimulateCrash();
1628 // Ensure that the RenderFrameProxyHost stays around and the RenderFrameProxy
1630 RenderFrameProxyHost
* render_frame_proxy_host
=
1631 opener1_manager
->GetRenderFrameProxyHost(rfh1
->GetSiteInstance());
1632 EXPECT_TRUE(render_frame_proxy_host
);
1633 EXPECT_FALSE(render_frame_proxy_host
->is_render_frame_proxy_live());
1635 // Expect the swapped out RVH to exist but not be live.
1637 opener1_manager
->GetSwappedOutRenderViewHost(rfh1
->GetSiteInstance()));
1639 opener1_manager
->GetSwappedOutRenderViewHost(rfh1
->GetSiteInstance())
1640 ->IsRenderViewLive());
1642 // Reload the initial tab. This should recreate the opener's swapped out RVH
1643 // in the original SiteInstance.
1644 contents()->GetController().Reload(true);
1645 contents()->GetMainFrame()->PrepareForCommit();
1647 opener1_manager
->GetSwappedOutRenderViewHost(rfh1
->GetSiteInstance())
1648 ->IsRenderViewLive());
1650 opener1_manager
->GetRoutingIdForSiteInstance(rfh1
->GetSiteInstance()),
1651 contents()->GetMainFrame()->GetRenderViewHost()->opener_frame_route_id());
1654 // Test that RenderViewHosts created for WebUI navigations are properly
1655 // granted WebUI bindings even if an unprivileged swapped out RenderViewHost
1656 // is in the same process (http://crbug.com/79918).
1657 TEST_F(RenderFrameHostManagerTest
, EnableWebUIWithSwappedOutOpener
) {
1658 set_should_create_webui(true);
1659 const GURL
kSettingsUrl("chrome://chrome/settings");
1660 const GURL
kPluginUrl("chrome://plugins");
1662 // Navigate to an initial WebUI URL.
1663 contents()->NavigateAndCommit(kSettingsUrl
);
1665 // Ensure the RVH has WebUI bindings.
1666 TestRenderViewHost
* rvh1
= test_rvh();
1667 EXPECT_TRUE(rvh1
->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1669 // Create a new tab and simulate it being the opener for the main
1670 // tab. It should be in the same SiteInstance.
1671 scoped_ptr
<TestWebContents
> opener1(
1672 TestWebContents::Create(browser_context(), rvh1
->GetSiteInstance()));
1673 RenderFrameHostManager
* opener1_manager
=
1674 opener1
->GetRenderManagerForTesting();
1675 contents()->SetOpener(opener1
.get());
1677 // Navigate to a different WebUI URL (different SiteInstance, same
1678 // BrowsingInstance).
1679 contents()->NavigateAndCommit(kPluginUrl
);
1680 TestRenderViewHost
* rvh2
= test_rvh();
1681 EXPECT_NE(rvh1
->GetSiteInstance(), rvh2
->GetSiteInstance());
1682 EXPECT_TRUE(rvh1
->GetSiteInstance()->IsRelatedSiteInstance(
1683 rvh2
->GetSiteInstance()));
1685 // Ensure a swapped out RFH and RVH is created in the first opener tab.
1686 RenderFrameProxyHost
* opener1_proxy
=
1687 opener1_manager
->GetRenderFrameProxyHost(rvh2
->GetSiteInstance());
1688 RenderFrameHostImpl
* opener1_rfh
= opener1_proxy
->render_frame_host();
1689 TestRenderViewHost
* opener1_rvh
= static_cast<TestRenderViewHost
*>(
1690 opener1_manager
->GetSwappedOutRenderViewHost(rvh2
->GetSiteInstance()));
1691 if (!SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
1692 EXPECT_TRUE(opener1_manager
->IsOnSwappedOutList(opener1_rfh
));
1693 EXPECT_TRUE(opener1_manager
->IsRVHOnSwappedOutList(opener1_rvh
));
1694 EXPECT_TRUE(opener1_rfh
->is_swapped_out());
1696 EXPECT_FALSE(opener1_rfh
);
1698 EXPECT_FALSE(opener1_rvh
->is_active());
1700 // Ensure the new RVH has WebUI bindings.
1701 EXPECT_TRUE(rvh2
->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1704 // Test that we reuse the same guest SiteInstance if we navigate across sites.
1705 TEST_F(RenderFrameHostManagerTest
, NoSwapOnGuestNavigations
) {
1706 GURL
guest_url(std::string(kGuestScheme
).append("://abc123"));
1707 SiteInstance
* instance
=
1708 SiteInstance::CreateForURL(browser_context(), guest_url
);
1709 scoped_ptr
<TestWebContents
> web_contents(
1710 TestWebContents::Create(browser_context(), instance
));
1712 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
1714 RenderFrameHostImpl
* host
= NULL
;
1716 // 1) The first navigation. --------------------------
1717 const GURL
kUrl1("http://www.google.com/");
1718 NavigationEntryImpl
entry1(
1719 NULL
/* instance */, -1 /* page_id */, kUrl1
, Referrer(),
1720 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED
,
1721 false /* is_renderer_init */);
1722 host
= NavigateToEntry(manager
, entry1
);
1724 // The RenderFrameHost created in Init will be reused.
1725 EXPECT_TRUE(host
== manager
->current_frame_host());
1726 EXPECT_FALSE(manager
->pending_frame_host());
1727 EXPECT_EQ(manager
->current_frame_host()->GetSiteInstance(), instance
);
1730 manager
->DidNavigateFrame(host
, true);
1731 // Commit to SiteInstance should be delayed until RenderFrame commit.
1732 EXPECT_EQ(host
, manager
->current_frame_host());
1734 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
1736 // 2) Navigate to a different domain. -------------------------
1737 // Guests stay in the same process on navigation.
1738 const GURL
kUrl2("http://www.chromium.org");
1739 NavigationEntryImpl
entry2(
1740 NULL
/* instance */, -1 /* page_id */, kUrl2
,
1741 Referrer(kUrl1
, blink::WebReferrerPolicyDefault
),
1742 base::string16() /* title */, ui::PAGE_TRANSITION_LINK
,
1743 true /* is_renderer_init */);
1744 host
= NavigateToEntry(manager
, entry2
);
1746 // The RenderFrameHost created in Init will be reused.
1747 EXPECT_EQ(host
, manager
->current_frame_host());
1748 EXPECT_FALSE(manager
->pending_frame_host());
1751 manager
->DidNavigateFrame(host
, true);
1752 EXPECT_EQ(host
, manager
->current_frame_host());
1754 EXPECT_EQ(host
->GetSiteInstance(), instance
);
1757 // Test that we cancel a pending RVH if we close the tab while it's pending.
1758 // http://crbug.com/294697.
1759 TEST_F(RenderFrameHostManagerTest
, NavigateWithEarlyClose
) {
1760 TestNotificationTracker notifications
;
1762 SiteInstance
* instance
= SiteInstance::Create(browser_context());
1764 BeforeUnloadFiredWebContentsDelegate delegate
;
1765 scoped_ptr
<TestWebContents
> web_contents(
1766 TestWebContents::Create(browser_context(), instance
));
1767 RenderViewHostChangedObserver
change_observer(web_contents
.get());
1768 web_contents
->SetDelegate(&delegate
);
1770 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
1772 // 1) The first navigation. --------------------------
1773 const GURL
kUrl1("http://www.google.com/");
1774 NavigationEntryImpl
entry1(NULL
/* instance */, -1 /* page_id */, kUrl1
,
1775 Referrer(), base::string16() /* title */,
1776 ui::PAGE_TRANSITION_TYPED
,
1777 false /* is_renderer_init */);
1778 RenderFrameHostImpl
* host
= NavigateToEntry(manager
, entry1
);
1780 // The RenderFrameHost created in Init will be reused.
1781 EXPECT_EQ(host
, manager
->current_frame_host());
1782 EXPECT_FALSE(GetPendingFrameHost(manager
));
1784 // We should observe RVH changed event.
1785 EXPECT_TRUE(change_observer
.DidHostChange());
1788 manager
->DidNavigateFrame(host
, true);
1790 // Commit to SiteInstance should be delayed until RenderFrame commits.
1791 EXPECT_EQ(host
, manager
->current_frame_host());
1792 EXPECT_FALSE(host
->GetSiteInstance()->HasSite());
1793 host
->GetSiteInstance()->SetSite(kUrl1
);
1795 // 2) Cross-site navigate to next site. -------------------------
1796 const GURL
kUrl2("http://www.example.com");
1797 NavigationEntryImpl
entry2(
1798 NULL
/* instance */, -1 /* page_id */, kUrl2
, Referrer(),
1799 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED
,
1800 false /* is_renderer_init */);
1801 RenderFrameHostImpl
* host2
= NavigateToEntry(manager
, entry2
);
1803 // A new RenderFrameHost should be created.
1804 ASSERT_EQ(host2
, GetPendingFrameHost(manager
));
1805 EXPECT_NE(host2
, host
);
1807 EXPECT_EQ(host
, manager
->current_frame_host());
1808 EXPECT_FALSE(manager
->current_frame_host()->is_swapped_out());
1809 EXPECT_EQ(host2
, GetPendingFrameHost(manager
));
1811 // 3) Close the tab. -------------------------
1812 notifications
.ListenFor(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED
,
1813 Source
<RenderWidgetHost
>(host2
->render_view_host()));
1814 manager
->OnBeforeUnloadACK(false, true, base::TimeTicks());
1817 notifications
.Check1AndReset(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED
));
1818 EXPECT_FALSE(GetPendingFrameHost(manager
));
1819 EXPECT_EQ(host
, manager
->current_frame_host());
1822 TEST_F(RenderFrameHostManagerTest
, CloseWithPendingWhileUnresponsive
) {
1823 const GURL
kUrl1("http://www.google.com/");
1824 const GURL
kUrl2("http://www.chromium.org/");
1826 CloseWebContentsDelegate close_delegate
;
1827 contents()->SetDelegate(&close_delegate
);
1829 // Navigate to the first page.
1830 contents()->NavigateAndCommit(kUrl1
);
1831 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1833 // Start to close the tab, but assume it's unresponsive.
1834 rfh1
->render_view_host()->ClosePage();
1835 EXPECT_TRUE(rfh1
->render_view_host()->is_waiting_for_close_ack());
1837 // Start a navigation to a new site.
1838 controller().LoadURL(
1839 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1840 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1841 switches::kEnableBrowserSideNavigation
)) {
1842 rfh1
->PrepareForCommit();
1844 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1846 // Simulate the unresponsiveness timer. The tab should close.
1847 contents()->RendererUnresponsive(rfh1
->render_view_host());
1848 EXPECT_TRUE(close_delegate
.is_closed());
1851 // Tests that the RenderFrameHost is properly deleted when the SwapOutACK is
1852 // received. (SwapOut and the corresponding ACK always occur after commit.)
1853 // Also tests that an early SwapOutACK is properly ignored.
1854 TEST_F(RenderFrameHostManagerTest
, DeleteFrameAfterSwapOutACK
) {
1855 const GURL
kUrl1("http://www.google.com/");
1856 const GURL
kUrl2("http://www.chromium.org/");
1858 // Navigate to the first page.
1859 contents()->NavigateAndCommit(kUrl1
);
1860 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1861 RenderFrameDeletedObserver
rfh_deleted_observer(rfh1
);
1862 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1864 // Navigate to new site, simulating onbeforeunload approval.
1865 controller().LoadURL(
1866 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1867 int entry_id
= controller().GetPendingEntry()->GetUniqueID();
1868 contents()->GetMainFrame()->PrepareForCommit();
1869 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1870 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1871 TestRenderFrameHost
* rfh2
= contents()->GetPendingMainFrame();
1873 // Simulate the swap out ack, unexpectedly early (before commit). It should
1875 rfh1
->OnSwappedOut();
1876 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1877 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1879 // The new page commits.
1880 contents()->TestDidNavigate(rfh2
, 1, entry_id
, true, kUrl2
,
1881 ui::PAGE_TRANSITION_TYPED
);
1882 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1883 EXPECT_EQ(rfh2
, contents()->GetMainFrame());
1884 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL
);
1885 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
1886 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
1888 rfh1
->frame_tree_node()->render_manager()->IsPendingDeletion(rfh1
));
1890 // Simulate the swap out ack.
1891 rfh1
->OnSwappedOut();
1893 // rfh1 should have been deleted.
1894 EXPECT_TRUE(rfh_deleted_observer
.deleted());
1898 // Tests that the RenderFrameHost is properly swapped out when the SwapOut ACK
1899 // is received. (SwapOut and the corresponding ACK always occur after commit.)
1900 TEST_F(RenderFrameHostManagerTest
, SwapOutFrameAfterSwapOutACK
) {
1901 const GURL
kUrl1("http://www.google.com/");
1902 const GURL
kUrl2("http://www.chromium.org/");
1904 // Navigate to the first page.
1905 contents()->NavigateAndCommit(kUrl1
);
1906 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1907 RenderFrameDeletedObserver
rfh_deleted_observer(rfh1
);
1908 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1910 // Increment the number of active frames in SiteInstanceImpl so that rfh1 is
1911 // not deleted on swap out.
1912 rfh1
->GetSiteInstance()->increment_active_frame_count();
1914 // Navigate to new site, simulating onbeforeunload approval.
1915 controller().LoadURL(
1916 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1917 int entry_id
= controller().GetPendingEntry()->GetUniqueID();
1918 contents()->GetMainFrame()->PrepareForCommit();
1919 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1920 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1921 TestRenderFrameHost
* rfh2
= contents()->GetPendingMainFrame();
1923 // The new page commits.
1924 contents()->TestDidNavigate(rfh2
, 1, entry_id
, true, kUrl2
,
1925 ui::PAGE_TRANSITION_TYPED
);
1926 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1927 EXPECT_EQ(rfh2
, contents()->GetMainFrame());
1928 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL
);
1929 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
1930 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
1932 // Simulate the swap out ack.
1933 rfh1
->OnSwappedOut();
1935 // rfh1 should be swapped out or deleted in --site-per-process.
1936 if (!SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
1937 EXPECT_FALSE(rfh_deleted_observer
.deleted());
1938 EXPECT_TRUE(rfh1
->is_swapped_out());
1940 EXPECT_TRUE(rfh_deleted_observer
.deleted());
1944 // Test that the RenderViewHost is properly swapped out if a navigation in the
1945 // new renderer commits before sending the SwapOut message to the old renderer.
1946 // This simulates a cross-site navigation to a synchronously committing URL
1947 // (e.g., a data URL) and ensures it works properly.
1948 TEST_F(RenderFrameHostManagerTest
,
1949 CommitNewNavigationBeforeSendingSwapOut
) {
1950 const GURL
kUrl1("http://www.google.com/");
1951 const GURL
kUrl2("http://www.chromium.org/");
1953 // Navigate to the first page.
1954 contents()->NavigateAndCommit(kUrl1
);
1955 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1956 RenderFrameDeletedObserver
rfh_deleted_observer(rfh1
);
1957 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1959 // Increment the number of active frames in SiteInstanceImpl so that rfh1 is
1960 // not deleted on swap out.
1961 scoped_refptr
<SiteInstanceImpl
> site_instance
= rfh1
->GetSiteInstance();
1962 site_instance
->increment_active_frame_count();
1964 // Navigate to new site, simulating onbeforeunload approval.
1965 controller().LoadURL(
1966 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1967 int entry_id
= controller().GetPendingEntry()->GetUniqueID();
1968 rfh1
->PrepareForCommit();
1969 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1970 TestRenderFrameHost
* rfh2
= contents()->GetPendingMainFrame();
1972 // The new page commits.
1973 contents()->TestDidNavigate(rfh2
, 1, entry_id
, true, kUrl2
,
1974 ui::PAGE_TRANSITION_TYPED
);
1975 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1976 EXPECT_EQ(rfh2
, contents()->GetMainFrame());
1977 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL
);
1978 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
1979 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
1981 // Simulate the swap out ack.
1982 rfh1
->OnSwappedOut();
1984 // rfh1 should be swapped out.
1985 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
1986 EXPECT_TRUE(rfh_deleted_observer
.deleted());
1987 EXPECT_TRUE(contents()->GetFrameTree()->root()->render_manager()
1988 ->GetRenderFrameProxyHost(site_instance
.get()));
1990 EXPECT_FALSE(rfh_deleted_observer
.deleted());
1991 EXPECT_TRUE(rfh1
->is_swapped_out());
1995 // Test that a RenderFrameHost is properly deleted when a cross-site navigation
1997 TEST_F(RenderFrameHostManagerTest
,
1998 CancelPendingProperlyDeletesOrSwaps
) {
1999 const GURL
kUrl1("http://www.google.com/");
2000 const GURL
kUrl2("http://www.chromium.org/");
2001 RenderFrameHostImpl
* pending_rfh
= NULL
;
2002 base::TimeTicks now
= base::TimeTicks::Now();
2004 // Navigate to the first page.
2005 contents()->NavigateAndCommit(kUrl1
);
2006 TestRenderFrameHost
* rfh1
= main_test_rfh();
2007 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
2009 // Navigate to a new site, starting a cross-site navigation.
2010 controller().LoadURL(
2011 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
2013 pending_rfh
= contents()->GetPendingMainFrame();
2014 RenderFrameDeletedObserver
rfh_deleted_observer(pending_rfh
);
2016 // Cancel the navigation by simulating a declined beforeunload dialog.
2017 contents()->GetMainFrame()->OnMessageReceived(
2018 FrameHostMsg_BeforeUnload_ACK(0, false, now
, now
));
2019 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
2021 // Since the pending RFH is the only one for the new SiteInstance, it should
2023 EXPECT_TRUE(rfh_deleted_observer
.deleted());
2026 // Start another cross-site navigation.
2027 controller().LoadURL(
2028 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
2030 pending_rfh
= contents()->GetPendingMainFrame();
2031 RenderFrameDeletedObserver
rfh_deleted_observer(pending_rfh
);
2033 // Increment the number of active frames in the new SiteInstance, which will
2034 // cause the pending RFH to be deleted and a RenderFrameProxyHost to be
2036 scoped_refptr
<SiteInstanceImpl
> site_instance
=
2037 pending_rfh
->GetSiteInstance();
2038 site_instance
->increment_active_frame_count();
2040 contents()->GetMainFrame()->OnMessageReceived(
2041 FrameHostMsg_BeforeUnload_ACK(0, false, now
, now
));
2042 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
2044 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
2045 EXPECT_TRUE(rfh_deleted_observer
.deleted());
2046 EXPECT_TRUE(contents()->GetFrameTree()->root()->render_manager()
2047 ->GetRenderFrameProxyHost(site_instance
.get()));
2049 EXPECT_FALSE(rfh_deleted_observer
.deleted());
2054 // Test that a pending RenderFrameHost in a non-root frame tree node is properly
2055 // deleted when the node is detached. Motivated by http://crbug.com/441357 and
2056 // http://crbug.com/444955.
2057 TEST_F(RenderFrameHostManagerTest
, DetachPendingChild
) {
2058 IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
2060 const GURL
kUrlA("http://www.google.com/");
2061 const GURL
kUrlB("http://webkit.org/");
2063 // Create a page with two child frames.
2064 contents()->NavigateAndCommit(kUrlA
);
2065 contents()->GetMainFrame()->OnCreateChildFrame(
2066 contents()->GetMainFrame()->GetProcess()->GetNextRoutingID(),
2067 blink::WebTreeScopeType::Document
, "frame_name",
2068 blink::WebSandboxFlags::None
);
2069 contents()->GetMainFrame()->OnCreateChildFrame(
2070 contents()->GetMainFrame()->GetProcess()->GetNextRoutingID(),
2071 blink::WebTreeScopeType::Document
, "frame_name",
2072 blink::WebSandboxFlags::None
);
2073 RenderFrameHostManager
* root_manager
=
2074 contents()->GetFrameTree()->root()->render_manager();
2075 RenderFrameHostManager
* iframe1
=
2076 contents()->GetFrameTree()->root()->child_at(0)->render_manager();
2077 RenderFrameHostManager
* iframe2
=
2078 contents()->GetFrameTree()->root()->child_at(1)->render_manager();
2080 // 1) The first navigation.
2081 NavigationEntryImpl
entryA(NULL
/* instance */, -1 /* page_id */, kUrlA
,
2082 Referrer(), base::string16() /* title */,
2083 ui::PAGE_TRANSITION_TYPED
,
2084 false /* is_renderer_init */);
2085 RenderFrameHostImpl
* host1
= NavigateToEntry(iframe1
, entryA
);
2087 // The RenderFrameHost created in Init will be reused.
2088 EXPECT_TRUE(host1
== iframe1
->current_frame_host());
2089 EXPECT_FALSE(GetPendingFrameHost(iframe1
));
2092 iframe1
->DidNavigateFrame(host1
, true);
2093 // Commit to SiteInstance should be delayed until RenderFrame commit.
2094 EXPECT_TRUE(host1
== iframe1
->current_frame_host());
2096 EXPECT_TRUE(host1
->GetSiteInstance()->HasSite());
2098 // 2) Cross-site navigate both frames to next site.
2099 NavigationEntryImpl
entryB(NULL
/* instance */, -1 /* page_id */, kUrlB
,
2100 Referrer(kUrlA
, blink::WebReferrerPolicyDefault
),
2101 base::string16() /* title */,
2102 ui::PAGE_TRANSITION_LINK
,
2103 false /* is_renderer_init */);
2104 host1
= NavigateToEntry(iframe1
, entryB
);
2105 RenderFrameHostImpl
* host2
= NavigateToEntry(iframe2
, entryB
);
2107 // A new, pending RenderFrameHost should be created in each FrameTreeNode.
2108 EXPECT_TRUE(GetPendingFrameHost(iframe1
));
2109 EXPECT_TRUE(GetPendingFrameHost(iframe2
));
2110 EXPECT_EQ(host1
, GetPendingFrameHost(iframe1
));
2111 EXPECT_EQ(host2
, GetPendingFrameHost(iframe2
));
2112 EXPECT_TRUE(RenderFrameHostImpl::IsRFHStateActive(
2113 GetPendingFrameHost(iframe1
)->rfh_state()));
2114 EXPECT_TRUE(RenderFrameHostImpl::IsRFHStateActive(
2115 GetPendingFrameHost(iframe2
)->rfh_state()));
2116 EXPECT_NE(GetPendingFrameHost(iframe1
), GetPendingFrameHost(iframe2
));
2117 EXPECT_EQ(GetPendingFrameHost(iframe1
)->GetSiteInstance(),
2118 GetPendingFrameHost(iframe2
)->GetSiteInstance());
2119 EXPECT_NE(iframe1
->current_frame_host(), GetPendingFrameHost(iframe1
));
2120 EXPECT_NE(iframe2
->current_frame_host(), GetPendingFrameHost(iframe2
));
2121 EXPECT_FALSE(contents()->CrossProcessNavigationPending())
2122 << "There should be no top-level pending navigation.";
2124 RenderFrameDeletedObserver
delete_watcher1(GetPendingFrameHost(iframe1
));
2125 RenderFrameDeletedObserver
delete_watcher2(GetPendingFrameHost(iframe2
));
2126 EXPECT_FALSE(delete_watcher1
.deleted());
2127 EXPECT_FALSE(delete_watcher2
.deleted());
2129 // Keep the SiteInstance alive for testing.
2130 scoped_refptr
<SiteInstanceImpl
> site_instance
=
2131 GetPendingFrameHost(iframe1
)->GetSiteInstance();
2132 EXPECT_TRUE(site_instance
->HasSite());
2133 EXPECT_NE(site_instance
, contents()->GetSiteInstance());
2134 EXPECT_EQ(2U, site_instance
->active_frame_count());
2136 // Proxies should exist.
2138 root_manager
->GetRenderFrameProxyHost(site_instance
.get()));
2140 iframe1
->GetRenderFrameProxyHost(site_instance
.get()));
2142 iframe2
->GetRenderFrameProxyHost(site_instance
.get()));
2144 // Detach the first child FrameTreeNode. This should kill the pending host but
2145 // not yet destroy proxies in |site_instance| since the other child remains.
2146 iframe1
->current_frame_host()->OnMessageReceived(
2147 FrameHostMsg_Detach(iframe1
->current_frame_host()->GetRoutingID()));
2148 iframe1
= NULL
; // Was just destroyed.
2150 EXPECT_TRUE(delete_watcher1
.deleted());
2151 EXPECT_FALSE(delete_watcher2
.deleted());
2152 EXPECT_EQ(1U, site_instance
->active_frame_count());
2154 // Proxies should still exist.
2156 root_manager
->GetRenderFrameProxyHost(site_instance
.get()));
2158 iframe2
->GetRenderFrameProxyHost(site_instance
.get()));
2160 // Detach the second child FrameTreeNode. This should trigger cleanup of
2161 // RenderFrameProxyHosts in |site_instance|.
2162 iframe2
->current_frame_host()->OnMessageReceived(
2163 FrameHostMsg_Detach(iframe2
->current_frame_host()->GetRoutingID()));
2164 iframe2
= NULL
; // Was just destroyed.
2166 EXPECT_TRUE(delete_watcher1
.deleted());
2167 EXPECT_TRUE(delete_watcher2
.deleted());
2169 EXPECT_EQ(0U, site_instance
->active_frame_count());
2171 root_manager
->GetRenderFrameProxyHost(site_instance
.get()))
2172 << "Proxies should have been cleaned up";
2173 EXPECT_TRUE(site_instance
->HasOneRef())
2174 << "This SiteInstance should be destroyable now.";
2177 // Two tabs in the same process crash. The first tab is reloaded, and the second
2178 // tab navigates away without reloading. The second tab's navigation shouldn't
2179 // mess with the first tab's content. Motivated by http://crbug.com/473714.
2180 TEST_F(RenderFrameHostManagerTest
, TwoTabsCrashOneReloadsOneLeaves
) {
2181 IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
2183 const GURL
kUrl1("http://www.google.com/");
2184 const GURL
kUrl2("http://webkit.org/");
2185 const GURL
kUrl3("http://whatwg.org/");
2187 // |contents1| and |contents2| navigate to the same page and then crash.
2188 TestWebContents
* contents1
= contents();
2189 scoped_ptr
<TestWebContents
> contents2(
2190 TestWebContents::Create(browser_context(), contents1
->GetSiteInstance()));
2191 contents1
->NavigateAndCommit(kUrl1
);
2192 contents2
->NavigateAndCommit(kUrl1
);
2193 MockRenderProcessHost
* rph
= contents1
->GetMainFrame()->GetProcess();
2194 EXPECT_EQ(rph
, contents2
->GetMainFrame()->GetProcess());
2195 rph
->SimulateCrash();
2196 EXPECT_FALSE(contents1
->GetMainFrame()->IsRenderFrameLive());
2197 EXPECT_FALSE(contents2
->GetMainFrame()->IsRenderFrameLive());
2198 EXPECT_EQ(contents1
->GetSiteInstance(), contents2
->GetSiteInstance());
2200 // Reload |contents1|.
2201 contents1
->NavigateAndCommit(kUrl1
);
2202 EXPECT_TRUE(contents1
->GetMainFrame()->IsRenderFrameLive());
2203 EXPECT_FALSE(contents2
->GetMainFrame()->IsRenderFrameLive());
2204 EXPECT_EQ(contents1
->GetSiteInstance(), contents2
->GetSiteInstance());
2206 // |contents1| creates an out of process iframe.
2207 contents1
->GetMainFrame()->OnCreateChildFrame(
2208 contents1
->GetMainFrame()->GetProcess()->GetNextRoutingID(),
2209 blink::WebTreeScopeType::Document
, "frame_name",
2210 blink::WebSandboxFlags::None
);
2211 RenderFrameHostManager
* iframe
=
2212 contents()->GetFrameTree()->root()->child_at(0)->render_manager();
2213 NavigationEntryImpl
entry(NULL
/* instance */, -1 /* page_id */, kUrl2
,
2214 Referrer(kUrl1
, blink::WebReferrerPolicyDefault
),
2215 base::string16() /* title */,
2216 ui::PAGE_TRANSITION_LINK
,
2217 false /* is_renderer_init */);
2218 RenderFrameHostImpl
* cross_site
= NavigateToEntry(iframe
, entry
);
2219 iframe
->DidNavigateFrame(cross_site
, true);
2221 // A proxy to the iframe should now exist in the SiteInstance of the main
2223 EXPECT_NE(cross_site
->GetSiteInstance(), contents1
->GetSiteInstance());
2225 iframe
->GetRenderFrameProxyHost(contents1
->GetSiteInstance()));
2227 iframe
->GetRenderFrameProxyHost(contents2
->GetSiteInstance()));
2229 // Navigate |contents2| away from the sad tab (and thus away from the
2230 // SiteInstance of |contents1|). This should not destroy the proxies needed by
2231 // |contents1| -- that was http://crbug.com/473714.
2232 EXPECT_FALSE(contents2
->GetMainFrame()->IsRenderFrameLive());
2233 contents2
->NavigateAndCommit(kUrl3
);
2234 EXPECT_TRUE(contents2
->GetMainFrame()->IsRenderFrameLive());
2236 iframe
->GetRenderFrameProxyHost(contents1
->GetSiteInstance()));
2238 iframe
->GetRenderFrameProxyHost(contents2
->GetSiteInstance()));
2241 } // namespace content