1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/command_line.h"
6 #include "base/files/file_path.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "base/test/histogram_tester.h"
9 #include "base/time/time.h"
10 #include "content/browser/frame_host/cross_site_transferring_request.h"
11 #include "content/browser/frame_host/navigation_controller_impl.h"
12 #include "content/browser/frame_host/navigation_entry_impl.h"
13 #include "content/browser/frame_host/navigation_request.h"
14 #include "content/browser/frame_host/navigator.h"
15 #include "content/browser/frame_host/render_frame_host_manager.h"
16 #include "content/browser/frame_host/render_frame_proxy_host.h"
17 #include "content/browser/site_instance_impl.h"
18 #include "content/browser/webui/web_ui_controller_factory_registry.h"
19 #include "content/common/frame_messages.h"
20 #include "content/common/view_messages.h"
21 #include "content/public/browser/notification_details.h"
22 #include "content/public/browser/notification_service.h"
23 #include "content/public/browser/notification_source.h"
24 #include "content/public/browser/notification_types.h"
25 #include "content/public/browser/render_process_host.h"
26 #include "content/public/browser/render_widget_host_iterator.h"
27 #include "content/public/browser/web_contents_delegate.h"
28 #include "content/public/browser/web_contents_observer.h"
29 #include "content/public/browser/web_ui_controller.h"
30 #include "content/public/common/bindings_policy.h"
31 #include "content/public/common/content_switches.h"
32 #include "content/public/common/javascript_message_type.h"
33 #include "content/public/common/url_constants.h"
34 #include "content/public/common/url_utils.h"
35 #include "content/public/test/mock_render_process_host.h"
36 #include "content/public/test/test_notification_tracker.h"
37 #include "content/test/test_content_browser_client.h"
38 #include "content/test/test_content_client.h"
39 #include "content/test/test_render_frame_host.h"
40 #include "content/test/test_render_view_host.h"
41 #include "content/test/test_web_contents.h"
42 #include "net/base/load_flags.h"
43 #include "testing/gtest/include/gtest/gtest.h"
44 #include "ui/base/page_transition_types.h"
49 class RenderFrameHostManagerTestWebUIControllerFactory
50 : public WebUIControllerFactory
{
52 RenderFrameHostManagerTestWebUIControllerFactory()
53 : should_create_webui_(false) {
55 ~RenderFrameHostManagerTestWebUIControllerFactory() override
{}
57 void set_should_create_webui(bool should_create_webui
) {
58 should_create_webui_
= should_create_webui
;
61 // WebUIFactory implementation.
62 WebUIController
* CreateWebUIControllerForURL(WebUI
* web_ui
,
63 const GURL
& url
) const override
{
64 if (!(should_create_webui_
&& HasWebUIScheme(url
)))
66 return new WebUIController(web_ui
);
69 WebUI::TypeID
GetWebUIType(BrowserContext
* browser_context
,
70 const GURL
& url
) const override
{
71 return WebUI::kNoWebUI
;
74 bool UseWebUIForURL(BrowserContext
* browser_context
,
75 const GURL
& url
) const override
{
76 return HasWebUIScheme(url
);
79 bool UseWebUIBindingsForURL(BrowserContext
* browser_context
,
80 const GURL
& url
) const override
{
81 return HasWebUIScheme(url
);
85 bool should_create_webui_
;
87 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostManagerTestWebUIControllerFactory
);
90 class BeforeUnloadFiredWebContentsDelegate
: public WebContentsDelegate
{
92 BeforeUnloadFiredWebContentsDelegate() {}
93 ~BeforeUnloadFiredWebContentsDelegate() override
{}
95 void BeforeUnloadFired(WebContents
* web_contents
,
97 bool* proceed_to_fire_unload
) override
{
98 *proceed_to_fire_unload
= proceed
;
102 DISALLOW_COPY_AND_ASSIGN(BeforeUnloadFiredWebContentsDelegate
);
105 class CloseWebContentsDelegate
: public WebContentsDelegate
{
107 CloseWebContentsDelegate() : close_called_(false) {}
108 ~CloseWebContentsDelegate() override
{}
110 void CloseContents(WebContents
* web_contents
) override
{
111 close_called_
= true;
114 bool is_closed() { return close_called_
; }
117 DISALLOW_COPY_AND_ASSIGN(CloseWebContentsDelegate
);
122 // This observer keeps track of the last deleted RenderViewHost to avoid
123 // accessing it and causing use-after-free condition.
124 class RenderViewHostDeletedObserver
: public WebContentsObserver
{
126 RenderViewHostDeletedObserver(RenderViewHost
* rvh
)
127 : WebContentsObserver(WebContents::FromRenderViewHost(rvh
)),
128 process_id_(rvh
->GetProcess()->GetID()),
129 routing_id_(rvh
->GetRoutingID()),
133 void RenderViewDeleted(RenderViewHost
* render_view_host
) override
{
134 if (render_view_host
->GetProcess()->GetID() == process_id_
&&
135 render_view_host
->GetRoutingID() == routing_id_
) {
149 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDeletedObserver
);
152 // This observer keeps track of the last created RenderFrameHost to allow tests
153 // to ensure that no RenderFrameHost objects are created when not expected.
154 class RenderFrameHostCreatedObserver
: public WebContentsObserver
{
156 RenderFrameHostCreatedObserver(WebContents
* web_contents
)
157 : WebContentsObserver(web_contents
),
161 void RenderFrameCreated(RenderFrameHost
* render_frame_host
) override
{
172 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostCreatedObserver
);
175 // This observer keeps track of the last deleted RenderFrameHost to avoid
176 // accessing it and causing use-after-free condition.
177 class RenderFrameHostDeletedObserver
: public WebContentsObserver
{
179 RenderFrameHostDeletedObserver(RenderFrameHost
* rfh
)
180 : WebContentsObserver(WebContents::FromRenderFrameHost(rfh
)),
181 process_id_(rfh
->GetProcess()->GetID()),
182 routing_id_(rfh
->GetRoutingID()),
186 void RenderFrameDeleted(RenderFrameHost
* render_frame_host
) override
{
187 if (render_frame_host
->GetProcess()->GetID() == process_id_
&&
188 render_frame_host
->GetRoutingID() == routing_id_
) {
202 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostDeletedObserver
);
205 // This observer is used to check whether IPC messages are being filtered for
206 // swapped out RenderFrameHost objects. It observes the plugin crash and favicon
207 // update events, which the FilterMessagesWhileSwappedOut test simulates being
208 // sent. The test is successful if the event is not observed.
209 // See http://crbug.com/351815
210 class PluginFaviconMessageObserver
: public WebContentsObserver
{
212 PluginFaviconMessageObserver(WebContents
* web_contents
)
213 : WebContentsObserver(web_contents
),
214 plugin_crashed_(false),
215 favicon_received_(false) { }
217 void PluginCrashed(const base::FilePath
& plugin_path
,
218 base::ProcessId plugin_pid
) override
{
219 plugin_crashed_
= true;
222 void DidUpdateFaviconURL(const std::vector
<FaviconURL
>& candidates
) override
{
223 favicon_received_
= true;
226 bool plugin_crashed() {
227 return plugin_crashed_
;
230 bool favicon_received() {
231 return favicon_received_
;
235 bool plugin_crashed_
;
236 bool favicon_received_
;
238 DISALLOW_COPY_AND_ASSIGN(PluginFaviconMessageObserver
);
243 class RenderFrameHostManagerTest
: public RenderViewHostImplTestHarness
{
245 void SetUp() override
{
246 RenderViewHostImplTestHarness::SetUp();
247 WebUIControllerFactory::RegisterFactory(&factory_
);
250 void TearDown() override
{
251 RenderViewHostImplTestHarness::TearDown();
252 WebUIControllerFactory::UnregisterFactoryForTesting(&factory_
);
255 void set_should_create_webui(bool should_create_webui
) {
256 factory_
.set_should_create_webui(should_create_webui
);
259 void NavigateActiveAndCommit(const GURL
& url
) {
260 // Note: we navigate the active RenderFrameHost because previous navigations
261 // won't have committed yet, so NavigateAndCommit does the wrong thing
263 controller().LoadURL(
264 url
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
266 // Simulate the BeforeUnload_ACK that is received from the current renderer
267 // for a cross-site navigation.
268 // PlzNavigate: it is necessary to call PrepareForCommit before getting the
269 // main and the pending frame because when we are trying to navigate to a
270 // WebUI from a new tab, a RenderFrameHost is created to display it that is
271 // committed immediately (since it is a new tab). Therefore the main frame
272 // is replaced without a pending frame being created, and we don't get the
273 // right values for the RFH to navigate: we try to use the old one that has
274 // been deleted in the meantime.
275 contents()->GetMainFrame()->PrepareForCommit();
277 TestRenderFrameHost
* old_rfh
= contents()->GetMainFrame();
278 TestRenderFrameHost
* active_rfh
= contents()->GetPendingMainFrame()
279 ? contents()->GetPendingMainFrame()
281 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, old_rfh
->rfh_state());
283 // Commit the navigation with a new page ID.
284 int32 max_page_id
= contents()->GetMaxPageIDForSiteInstance(
285 active_rfh
->GetSiteInstance());
287 // Use an observer to avoid accessing a deleted renderer later on when the
288 // state is being checked.
289 RenderFrameHostDeletedObserver
rfh_observer(old_rfh
);
290 RenderViewHostDeletedObserver
rvh_observer(old_rfh
->GetRenderViewHost());
291 active_rfh
->SendNavigate(max_page_id
+ 1, url
);
293 // Make sure that we start to run the unload handler at the time of commit.
294 bool expecting_rfh_shutdown
= false;
295 if (old_rfh
!= active_rfh
&& !rfh_observer
.deleted()) {
296 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
,
297 old_rfh
->rfh_state());
298 if (!old_rfh
->GetSiteInstance()->active_frame_count()) {
299 expecting_rfh_shutdown
= true;
301 old_rfh
->frame_tree_node()->render_manager()->IsPendingDeletion(
306 // Simulate the swap out ACK coming from the pending renderer. This should
307 // either shut down the old RFH or leave it in a swapped out state.
308 if (old_rfh
!= active_rfh
) {
309 old_rfh
->OnSwappedOut();
310 if (expecting_rfh_shutdown
) {
311 EXPECT_TRUE(rfh_observer
.deleted());
312 EXPECT_TRUE(rvh_observer
.deleted());
314 EXPECT_EQ(RenderFrameHostImpl::STATE_SWAPPED_OUT
,
315 old_rfh
->rfh_state());
318 EXPECT_EQ(active_rfh
, contents()->GetMainFrame());
319 EXPECT_EQ(NULL
, contents()->GetPendingMainFrame());
322 bool ShouldSwapProcesses(RenderFrameHostManager
* manager
,
323 const NavigationEntryImpl
* current_entry
,
324 const NavigationEntryImpl
* new_entry
) const {
326 BrowserContext
* browser_context
=
327 manager
->delegate_
->GetControllerForRenderManager().GetBrowserContext();
328 const GURL
& current_effective_url
= current_entry
?
329 SiteInstanceImpl::GetEffectiveURL(browser_context
,
330 current_entry
->GetURL()) :
331 manager
->render_frame_host_
->GetSiteInstance()->GetSiteURL();
332 bool current_is_view_source_mode
= current_entry
?
333 current_entry
->IsViewSourceMode() : new_entry
->IsViewSourceMode();
334 return manager
->ShouldSwapBrowsingInstancesForNavigation(
335 current_effective_url
,
336 current_is_view_source_mode
,
337 new_entry
->site_instance(),
338 SiteInstanceImpl::GetEffectiveURL(browser_context
, new_entry
->GetURL()),
339 new_entry
->IsViewSourceMode());
342 // Creates a test RenderFrameHost that's swapped out.
343 TestRenderFrameHost
* CreateSwappedOutRenderFrameHost() {
344 const GURL
kChromeURL("chrome://foo");
345 const GURL
kDestUrl("http://www.google.com/");
347 // Navigate our first tab to a chrome url and then to the destination.
348 NavigateActiveAndCommit(kChromeURL
);
349 TestRenderFrameHost
* ntp_rfh
= contents()->GetMainFrame();
351 // Navigate to a cross-site URL.
352 contents()->GetController().LoadURL(
353 kDestUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
354 contents()->GetMainFrame()->PrepareForCommit();
355 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
357 // Manually increase the number of active frames in the
358 // SiteInstance that ntp_rfh belongs to, to prevent it from being
359 // destroyed when it gets swapped out.
360 ntp_rfh
->GetSiteInstance()->increment_active_frame_count();
362 TestRenderFrameHost
* dest_rfh
= contents()->GetPendingMainFrame();
364 EXPECT_NE(ntp_rfh
, dest_rfh
);
366 // BeforeUnload finishes.
367 ntp_rfh
->SendBeforeUnloadACK(true);
369 dest_rfh
->SendNavigate(101, kDestUrl
);
370 ntp_rfh
->OnSwappedOut();
372 EXPECT_TRUE(ntp_rfh
->is_swapped_out());
376 // Returns the RenderFrameHost that should be used in the navigation to
378 RenderFrameHostImpl
* NavigateToEntry(
379 RenderFrameHostManager
* manager
,
380 const NavigationEntryImpl
& entry
) {
381 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
382 switches::kEnableBrowserSideNavigation
)) {
383 scoped_ptr
<NavigationRequest
> navigation_request
=
384 NavigationRequest::CreateBrowserInitiated(
385 manager
->frame_tree_node_
, entry
, FrameMsg_Navigate_Type::NORMAL
,
386 base::TimeTicks::Now(),
387 static_cast<NavigationControllerImpl
*>(&controller()));
388 TestRenderFrameHost
* frame_host
= static_cast<TestRenderFrameHost
*>(
389 manager
->GetFrameHostForNavigation(*navigation_request
));
391 frame_host
->set_pending_commit(true);
394 return manager
->Navigate(entry
);
397 // Returns the pending RenderFrameHost.
398 // PlzNavigate: returns the speculative RenderFrameHost.
399 RenderFrameHostImpl
* GetPendingFrameHost(
400 RenderFrameHostManager
* manager
) {
401 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
402 switches::kEnableBrowserSideNavigation
)) {
403 return manager
->speculative_render_frame_host_
.get();
405 return manager
->pending_frame_host();
409 RenderFrameHostManagerTestWebUIControllerFactory factory_
;
412 // Tests that when you navigate from a chrome:// url to another page, and
413 // then do that same thing in another tab, that the two resulting pages have
414 // different SiteInstances, BrowsingInstances, and RenderProcessHosts. This is
415 // a regression test for bug 9364.
416 TEST_F(RenderFrameHostManagerTest
, NewTabPageProcesses
) {
417 set_should_create_webui(true);
418 const GURL
kChromeUrl("chrome://foo");
419 const GURL
kDestUrl("http://www.google.com/");
421 // Navigate our first tab to the chrome url and then to the destination,
422 // ensuring we grant bindings to the chrome URL.
423 NavigateActiveAndCommit(kChromeUrl
);
424 EXPECT_TRUE(active_rvh()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
425 NavigateActiveAndCommit(kDestUrl
);
427 EXPECT_FALSE(contents()->GetPendingMainFrame());
429 // Make a second tab.
430 scoped_ptr
<TestWebContents
> contents2(
431 TestWebContents::Create(browser_context(), NULL
));
433 // Load the two URLs in the second tab. Note that the first navigation creates
434 // a RFH that's not pending (since there is no cross-site transition), so
435 // we use the committed one.
436 contents2
->GetController().LoadURL(
437 kChromeUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
438 contents2
->GetMainFrame()->PrepareForCommit();
439 TestRenderFrameHost
* ntp_rfh2
= contents2
->GetMainFrame();
440 EXPECT_FALSE(contents2
->CrossProcessNavigationPending());
441 ntp_rfh2
->SendNavigate(100, kChromeUrl
);
443 // The second one is the opposite, creating a cross-site transition and
444 // requiring a beforeunload ack.
445 contents2
->GetController().LoadURL(
446 kDestUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
447 contents2
->GetMainFrame()->PrepareForCommit();
448 EXPECT_TRUE(contents2
->CrossProcessNavigationPending());
449 TestRenderFrameHost
* dest_rfh2
= contents2
->GetPendingMainFrame();
450 ASSERT_TRUE(dest_rfh2
);
452 dest_rfh2
->SendNavigate(101, kDestUrl
);
454 // The two RFH's should be different in every way.
455 EXPECT_NE(contents()->GetMainFrame()->GetProcess(), dest_rfh2
->GetProcess());
456 EXPECT_NE(contents()->GetMainFrame()->GetSiteInstance(),
457 dest_rfh2
->GetSiteInstance());
458 EXPECT_FALSE(dest_rfh2
->GetSiteInstance()->IsRelatedSiteInstance(
459 contents()->GetMainFrame()->GetSiteInstance()));
461 // Navigate both to the new tab page, and verify that they share a
462 // RenderProcessHost (not a SiteInstance).
463 NavigateActiveAndCommit(kChromeUrl
);
464 EXPECT_FALSE(contents()->GetPendingMainFrame());
466 contents2
->GetController().LoadURL(
467 kChromeUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
468 contents2
->GetMainFrame()->PrepareForCommit();
469 contents2
->GetPendingMainFrame()->SendNavigate(102, kChromeUrl
);
471 EXPECT_NE(contents()->GetMainFrame()->GetSiteInstance(),
472 contents2
->GetMainFrame()->GetSiteInstance());
473 EXPECT_EQ(contents()->GetMainFrame()->GetSiteInstance()->GetProcess(),
474 contents2
->GetMainFrame()->GetSiteInstance()->GetProcess());
477 // Ensure that the browser ignores most IPC messages that arrive from a
478 // RenderViewHost that has been swapped out. We do not want to take
479 // action on requests from a non-active renderer. The main exception is
480 // for synchronous messages, which cannot be ignored without leaving the
481 // renderer in a stuck state. See http://crbug.com/93427.
482 TEST_F(RenderFrameHostManagerTest
, FilterMessagesWhileSwappedOut
) {
483 const GURL
kChromeURL("chrome://foo");
484 const GURL
kDestUrl("http://www.google.com/");
485 std::vector
<FaviconURL
> icons
;
487 // Navigate our first tab to a chrome url and then to the destination.
488 NavigateActiveAndCommit(kChromeURL
);
489 TestRenderFrameHost
* ntp_rfh
= contents()->GetMainFrame();
491 // Send an update favicon message and make sure it works.
493 PluginFaviconMessageObserver
observer(contents());
494 EXPECT_TRUE(ntp_rfh
->GetRenderViewHost()->OnMessageReceived(
495 ViewHostMsg_UpdateFaviconURL(
496 ntp_rfh
->GetRenderViewHost()->GetRoutingID(), icons
)));
497 EXPECT_TRUE(observer
.favicon_received());
499 // Create one more frame in the same SiteInstance where ntp_rfh
500 // exists so that it doesn't get deleted on navigation to another
502 ntp_rfh
->GetSiteInstance()->increment_active_frame_count();
504 // Navigate to a cross-site URL.
505 NavigateActiveAndCommit(kDestUrl
);
506 TestRenderFrameHost
* dest_rfh
= contents()->GetMainFrame();
507 ASSERT_TRUE(dest_rfh
);
508 EXPECT_NE(ntp_rfh
, dest_rfh
);
510 // The new RVH should be able to update its favicon.
512 PluginFaviconMessageObserver
observer(contents());
514 dest_rfh
->GetRenderViewHost()->OnMessageReceived(
515 ViewHostMsg_UpdateFaviconURL(
516 dest_rfh
->GetRenderViewHost()->GetRoutingID(), icons
)));
517 EXPECT_TRUE(observer
.favicon_received());
520 // The old renderer, being slow, now updates the favicon. It should be
521 // filtered out and not take effect.
522 EXPECT_TRUE(ntp_rfh
->is_swapped_out());
524 PluginFaviconMessageObserver
observer(contents());
526 ntp_rfh
->GetRenderViewHost()->OnMessageReceived(
527 ViewHostMsg_UpdateFaviconURL(
528 dest_rfh
->GetRenderViewHost()->GetRoutingID(), icons
)));
529 EXPECT_FALSE(observer
.favicon_received());
532 #if defined(ENABLE_PLUGINS)
533 // The same logic should apply to RenderFrameHosts as well and routing through
534 // swapped out RFH shouldn't be allowed. Use a PluginCrashObserver to check
535 // if the IPC message is allowed through or not.
537 PluginFaviconMessageObserver
observer(contents());
538 EXPECT_TRUE(ntp_rfh
->OnMessageReceived(
539 FrameHostMsg_PluginCrashed(
540 ntp_rfh
->GetRoutingID(), base::FilePath(), 0)));
541 EXPECT_FALSE(observer
.plugin_crashed());
545 // We cannot filter out synchronous IPC messages, because the renderer would
546 // be left waiting for a reply. We pick RunBeforeUnloadConfirm as an example
547 // that can run easily within a unit test, and that needs to receive a reply
548 // without showing an actual dialog.
549 MockRenderProcessHost
* ntp_process_host
= ntp_rfh
->GetProcess();
550 ntp_process_host
->sink().ClearMessages();
551 const base::string16 msg
= base::ASCIIToUTF16("Message");
553 base::string16 unused
;
554 FrameHostMsg_RunBeforeUnloadConfirm
before_unload_msg(
555 ntp_rfh
->GetRoutingID(), kChromeURL
, msg
, false, &result
, &unused
);
556 // Enable pumping for check in BrowserMessageFilter::CheckCanDispatchOnUI.
557 before_unload_msg
.EnableMessagePumping();
558 EXPECT_TRUE(ntp_rfh
->OnMessageReceived(before_unload_msg
));
559 EXPECT_TRUE(ntp_process_host
->sink().GetUniqueMessageMatching(IPC_REPLY_ID
));
561 // Also test RunJavaScriptMessage.
562 ntp_process_host
->sink().ClearMessages();
563 FrameHostMsg_RunJavaScriptMessage
js_msg(
564 ntp_rfh
->GetRoutingID(), msg
, msg
, kChromeURL
,
565 JAVASCRIPT_MESSAGE_TYPE_CONFIRM
, &result
, &unused
);
566 js_msg
.EnableMessagePumping();
567 EXPECT_TRUE(ntp_rfh
->OnMessageReceived(js_msg
));
568 EXPECT_TRUE(ntp_process_host
->sink().GetUniqueMessageMatching(IPC_REPLY_ID
));
571 // Test that the ViewHostMsg_UpdateFaviconURL IPC message is ignored if the
572 // renderer is in the STATE_PENDING_SWAP_OUT_STATE. The favicon code assumes
573 // that it only gets ViewHostMsg_UpdateFaviconURL messages for the most recently
574 // committed navigation for each WebContentsImpl.
575 TEST_F(RenderFrameHostManagerTest
, UpdateFaviconURLWhilePendingSwapOut
) {
576 const GURL
kChromeURL("chrome://foo");
577 const GURL
kDestUrl("http://www.google.com/");
578 std::vector
<FaviconURL
> icons
;
580 // Navigate our first tab to a chrome url and then to the destination.
581 NavigateActiveAndCommit(kChromeURL
);
582 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
584 // Send an update favicon message and make sure it works.
586 PluginFaviconMessageObserver
observer(contents());
587 EXPECT_TRUE(rfh1
->GetRenderViewHost()->OnMessageReceived(
588 ViewHostMsg_UpdateFaviconURL(
589 rfh1
->GetRenderViewHost()->GetRoutingID(), icons
)));
590 EXPECT_TRUE(observer
.favicon_received());
593 // Create one more frame in the same SiteInstance where |rfh1| exists so that
594 // it doesn't get deleted on navigation to another site.
595 rfh1
->GetSiteInstance()->increment_active_frame_count();
597 // Navigate to a cross-site URL and commit the new page.
598 controller().LoadURL(
599 kDestUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
600 contents()->GetMainFrame()->PrepareForCommit();
601 TestRenderFrameHost
* rfh2
= contents()->GetPendingMainFrame();
602 contents()->TestDidNavigate(rfh2
, 1, kDestUrl
, ui::PAGE_TRANSITION_TYPED
);
603 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
604 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
606 // The new RVH should be able to update its favicons.
608 PluginFaviconMessageObserver
observer(contents());
609 EXPECT_TRUE(rfh2
->GetRenderViewHost()->OnMessageReceived(
610 ViewHostMsg_UpdateFaviconURL(rfh2
->GetRenderViewHost()->GetRoutingID(),
612 EXPECT_TRUE(observer
.favicon_received());
615 // The old renderer, being slow, now updates its favicons. The message should
618 PluginFaviconMessageObserver
observer(contents());
619 EXPECT_TRUE(rfh1
->GetRenderViewHost()->OnMessageReceived(
620 ViewHostMsg_UpdateFaviconURL(rfh1
->GetRenderViewHost()->GetRoutingID(),
622 EXPECT_FALSE(observer
.favicon_received());
626 // Ensure that frames aren't added to the frame tree, if the message is coming
627 // from a process different than the parent frame's current RenderFrameHost
628 // process. Otherwise it is possible to have collisions of routing ids, as they
629 // are scoped per process. See https://crbug.com/415059.
630 TEST_F(RenderFrameHostManagerTest
, DropCreateChildFrameWhileSwappedOut
) {
631 const GURL
kUrl1("http://foo.com");
632 const GURL
kUrl2("http://www.google.com/");
634 // Navigate to the first site.
635 NavigateActiveAndCommit(kUrl1
);
636 TestRenderFrameHost
* initial_rfh
= contents()->GetMainFrame();
638 RenderFrameHostCreatedObserver
observer(contents());
639 initial_rfh
->OnCreateChildFrame(
640 initial_rfh
->GetProcess()->GetNextRoutingID(), std::string(),
642 EXPECT_TRUE(observer
.created());
645 // Create one more frame in the same SiteInstance where initial_rfh
646 // exists so that initial_rfh doesn't get deleted on navigation to another
648 initial_rfh
->GetSiteInstance()->increment_active_frame_count();
650 // Navigate to a cross-site URL.
651 NavigateActiveAndCommit(kUrl2
);
652 EXPECT_TRUE(initial_rfh
->is_swapped_out());
654 TestRenderFrameHost
* dest_rfh
= contents()->GetMainFrame();
655 ASSERT_TRUE(dest_rfh
);
656 EXPECT_NE(initial_rfh
, dest_rfh
);
659 // Since the old RFH is now swapped out, it shouldn't process any messages
660 // to create child frames.
661 RenderFrameHostCreatedObserver
observer(contents());
662 initial_rfh
->OnCreateChildFrame(
663 initial_rfh
->GetProcess()->GetNextRoutingID(), std::string(),
665 EXPECT_FALSE(observer
.created());
669 TEST_F(RenderFrameHostManagerTest
, WhiteListSwapCompositorFrame
) {
670 TestRenderFrameHost
* swapped_out_rfh
= CreateSwappedOutRenderFrameHost();
671 TestRenderWidgetHostView
* swapped_out_rwhv
=
672 static_cast<TestRenderWidgetHostView
*>(
673 swapped_out_rfh
->GetRenderViewHost()->GetView());
674 EXPECT_FALSE(swapped_out_rwhv
->did_swap_compositor_frame());
676 MockRenderProcessHost
* process_host
= swapped_out_rfh
->GetProcess();
677 process_host
->sink().ClearMessages();
679 cc::CompositorFrame frame
;
680 ViewHostMsg_SwapCompositorFrame
msg(
681 rvh()->GetRoutingID(), 0, frame
, std::vector
<IPC::Message
>());
683 EXPECT_TRUE(swapped_out_rfh
->render_view_host()->OnMessageReceived(msg
));
684 EXPECT_TRUE(swapped_out_rwhv
->did_swap_compositor_frame());
687 // Test if RenderViewHost::GetRenderWidgetHosts() only returns active
689 TEST_F(RenderFrameHostManagerTest
, GetRenderWidgetHostsReturnsActiveViews
) {
690 TestRenderFrameHost
* swapped_out_rfh
= CreateSwappedOutRenderFrameHost();
691 EXPECT_TRUE(swapped_out_rfh
->is_swapped_out());
693 scoped_ptr
<RenderWidgetHostIterator
> widgets(
694 RenderWidgetHost::GetRenderWidgetHosts());
695 // We know that there is the only one active widget. Another view is
696 // now swapped out, so the swapped out view is not included in the
698 RenderWidgetHost
* widget
= widgets
->GetNextHost();
699 EXPECT_FALSE(widgets
->GetNextHost());
700 RenderViewHost
* rvh
= RenderViewHost::From(widget
);
701 EXPECT_TRUE(static_cast<RenderViewHostImpl
*>(rvh
)->is_active());
704 // Test if RenderViewHost::GetRenderWidgetHosts() returns a subset of
705 // RenderViewHostImpl::GetAllRenderWidgetHosts().
706 // RenderViewHost::GetRenderWidgetHosts() returns only active widgets, but
707 // RenderViewHostImpl::GetAllRenderWidgetHosts() returns everything
708 // including swapped out ones.
709 TEST_F(RenderFrameHostManagerTest
,
710 GetRenderWidgetHostsWithinGetAllRenderWidgetHosts
) {
711 TestRenderFrameHost
* swapped_out_rfh
= CreateSwappedOutRenderFrameHost();
712 EXPECT_TRUE(swapped_out_rfh
->is_swapped_out());
714 scoped_ptr
<RenderWidgetHostIterator
> widgets(
715 RenderWidgetHost::GetRenderWidgetHosts());
717 while (RenderWidgetHost
* w
= widgets
->GetNextHost()) {
719 scoped_ptr
<RenderWidgetHostIterator
> all_widgets(
720 RenderWidgetHostImpl::GetAllRenderWidgetHosts());
721 while (RenderWidgetHost
* widget
= all_widgets
->GetNextHost()) {
731 // Test if SiteInstanceImpl::active_frame_count() is correctly updated
732 // as frames in a SiteInstance get swapped out and in.
733 TEST_F(RenderFrameHostManagerTest
, ActiveFrameCountWhileSwappingInAndOut
) {
734 const GURL
kUrl1("http://www.google.com/");
735 const GURL
kUrl2("http://www.chromium.org/");
737 // Navigate to an initial URL.
738 contents()->NavigateAndCommit(kUrl1
);
739 TestRenderFrameHost
* rfh1
= main_test_rfh();
741 SiteInstanceImpl
* instance1
= rfh1
->GetSiteInstance();
742 EXPECT_EQ(instance1
->active_frame_count(), 1U);
744 // Create 2 new tabs and simulate them being the opener chain for the main
745 // tab. They should be in the same SiteInstance.
746 scoped_ptr
<TestWebContents
> opener1(
747 TestWebContents::Create(browser_context(), instance1
));
748 contents()->SetOpener(opener1
.get());
750 scoped_ptr
<TestWebContents
> opener2(
751 TestWebContents::Create(browser_context(), instance1
));
752 opener1
->SetOpener(opener2
.get());
754 EXPECT_EQ(instance1
->active_frame_count(), 3U);
756 // Navigate to a cross-site URL (different SiteInstance but same
757 // BrowsingInstance).
758 contents()->NavigateAndCommit(kUrl2
);
759 TestRenderFrameHost
* rfh2
= main_test_rfh();
760 SiteInstanceImpl
* instance2
= rfh2
->GetSiteInstance();
762 // rvh2 is on chromium.org which is different from google.com on
763 // which other tabs are.
764 EXPECT_EQ(instance2
->active_frame_count(), 1U);
766 // There are two active views on google.com now.
767 EXPECT_EQ(instance1
->active_frame_count(), 2U);
769 // Navigate to the original origin (google.com).
770 contents()->NavigateAndCommit(kUrl1
);
772 EXPECT_EQ(instance1
->active_frame_count(), 3U);
775 // This deletes a WebContents when the given RVH is deleted. This is
776 // only for testing whether deleting an RVH does not cause any UaF in
777 // other parts of the system. For now, this class is only used for the
778 // next test cases to detect the bug mentioned at
779 // http://crbug.com/259859.
780 class RenderViewHostDestroyer
: public WebContentsObserver
{
782 RenderViewHostDestroyer(RenderViewHost
* render_view_host
,
783 WebContents
* web_contents
)
784 : WebContentsObserver(WebContents::FromRenderViewHost(render_view_host
)),
785 render_view_host_(render_view_host
),
786 web_contents_(web_contents
) {}
788 void RenderViewDeleted(RenderViewHost
* render_view_host
) override
{
789 if (render_view_host
== render_view_host_
)
790 delete web_contents_
;
794 RenderViewHost
* render_view_host_
;
795 WebContents
* web_contents_
;
797 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestroyer
);
800 // Test if ShutdownRenderViewHostsInSiteInstance() does not touch any
801 // RenderWidget that has been freed while deleting a RenderViewHost in
802 // a previous iteration. This is a regression test for
803 // http://crbug.com/259859.
804 TEST_F(RenderFrameHostManagerTest
,
805 DetectUseAfterFreeInShutdownRenderViewHostsInSiteInstance
) {
806 const GURL
kChromeURL("chrome://newtab");
807 const GURL
kUrl1("http://www.google.com");
808 const GURL
kUrl2("http://www.chromium.org");
810 // Navigate our first tab to a chrome url and then to the destination.
811 NavigateActiveAndCommit(kChromeURL
);
812 TestRenderFrameHost
* ntp_rfh
= contents()->GetMainFrame();
814 // Create one more tab and navigate to kUrl1. web_contents is not
815 // wrapped as scoped_ptr since it intentionally deleted by destroyer
816 // below as part of this test.
817 TestWebContents
* web_contents
=
818 TestWebContents::Create(browser_context(), ntp_rfh
->GetSiteInstance());
819 web_contents
->NavigateAndCommit(kUrl1
);
820 RenderViewHostDestroyer
destroyer(ntp_rfh
->GetRenderViewHost(),
823 // This causes the first tab to navigate to kUrl2, which destroys
824 // the ntp_rfh in ShutdownRenderViewHostsInSiteInstance(). When
825 // ntp_rfh is destroyed, it also destroys the RVHs in web_contents
826 // too. This can test whether
827 // SiteInstanceImpl::ShutdownRenderViewHostsInSiteInstance() can
828 // touch any object freed in this way or not while iterating through
830 contents()->NavigateAndCommit(kUrl2
);
833 // When there is an error with the specified page, renderer exits view-source
834 // mode. See WebFrameImpl::DidFail(). We check by this test that
835 // EnableViewSourceMode message is sent on every navigation regardless
836 // RenderView is being newly created or reused.
837 TEST_F(RenderFrameHostManagerTest
, AlwaysSendEnableViewSourceMode
) {
838 const GURL
kChromeUrl("chrome://foo/");
839 const GURL
kUrl("http://foo/");
840 const GURL
kViewSourceUrl("view-source:http://foo/");
842 // We have to navigate to some page at first since without this, the first
843 // navigation will reuse the SiteInstance created by Init(), and the second
844 // one will create a new SiteInstance. Because current_instance and
845 // new_instance will be different, a new RenderViewHost will be created for
846 // the second navigation. We have to avoid this in order to exercise the
848 NavigateActiveAndCommit(kChromeUrl
);
850 // Navigate. Note that "view source" URLs are implemented by putting the RFH
851 // into a view-source mode and then navigating to the inner URL, so that's why
852 // the bare URL is what's committed and returned by the last committed entry's
854 controller().LoadURL(
855 kViewSourceUrl
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
857 // Simulate response from RenderFrame for DispatchBeforeUnload.
858 contents()->GetMainFrame()->PrepareForCommit();
859 ASSERT_TRUE(contents()->GetPendingMainFrame())
860 << "Expected new pending RenderFrameHost to be created.";
861 RenderFrameHost
* last_rfh
= contents()->GetPendingMainFrame();
863 contents()->GetMaxPageIDForSiteInstance(last_rfh
->GetSiteInstance()) + 1;
864 contents()->GetPendingMainFrame()->SendNavigate(new_id
, kUrl
);
866 EXPECT_EQ(1, controller().GetLastCommittedEntryIndex());
867 NavigationEntry
* last_committed
= controller().GetLastCommittedEntry();
868 ASSERT_NE(nullptr, last_committed
);
869 EXPECT_EQ(kUrl
, last_committed
->GetURL());
870 EXPECT_EQ(kViewSourceUrl
, last_committed
->GetVirtualURL());
871 EXPECT_FALSE(controller().GetPendingEntry());
872 // Because we're using TestWebContents and TestRenderViewHost in this
873 // unittest, no one calls WebContentsImpl::RenderViewCreated(). So, we see no
874 // EnableViewSourceMode message, here.
876 // Clear queued messages before load.
877 process()->sink().ClearMessages();
880 controller().LoadURL(
881 kViewSourceUrl
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
882 contents()->GetMainFrame()->PrepareForCommit();
884 // The same RenderViewHost should be reused.
885 EXPECT_FALSE(contents()->GetPendingMainFrame());
886 EXPECT_EQ(last_rfh
, contents()->GetMainFrame());
888 // The renderer sends a commit.
889 contents()->GetMainFrame()->SendNavigate(new_id
, kUrl
);
890 EXPECT_EQ(1, controller().GetLastCommittedEntryIndex());
891 EXPECT_FALSE(controller().GetPendingEntry());
893 // New message should be sent out to make sure to enter view-source mode.
894 EXPECT_TRUE(process()->sink().GetUniqueMessageMatching(
895 ViewMsg_EnableViewSourceMode::ID
));
898 // Tests the Init function by checking the initial RenderViewHost.
899 TEST_F(RenderFrameHostManagerTest
, Init
) {
900 // Using TestBrowserContext.
901 SiteInstanceImpl
* instance
=
902 static_cast<SiteInstanceImpl
*>(SiteInstance::Create(browser_context()));
903 EXPECT_FALSE(instance
->HasSite());
905 scoped_ptr
<TestWebContents
> web_contents(
906 TestWebContents::Create(browser_context(), instance
));
908 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
909 RenderViewHostImpl
* rvh
= manager
->current_host();
910 RenderFrameHostImpl
* rfh
= manager
->current_frame_host();
913 EXPECT_EQ(rvh
, rfh
->render_view_host());
914 EXPECT_EQ(instance
, rvh
->GetSiteInstance());
915 EXPECT_EQ(web_contents
.get(), rvh
->GetDelegate());
916 EXPECT_EQ(web_contents
.get(), rfh
->delegate());
917 EXPECT_TRUE(manager
->GetRenderWidgetHostView());
918 EXPECT_FALSE(manager
->pending_render_view_host());
921 // Tests the Navigate function. We navigate three sites consecutively and check
922 // how the pending/committed RenderViewHost are modified.
923 TEST_F(RenderFrameHostManagerTest
, Navigate
) {
924 TestNotificationTracker notifications
;
926 SiteInstance
* instance
= SiteInstance::Create(browser_context());
928 scoped_ptr
<TestWebContents
> web_contents(
929 TestWebContents::Create(browser_context(), instance
));
930 notifications
.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED
,
931 Source
<WebContents
>(web_contents
.get()));
933 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
934 RenderFrameHostImpl
* host
= NULL
;
936 // 1) The first navigation. --------------------------
937 const GURL
kUrl1("http://www.google.com/");
938 NavigationEntryImpl
entry1(
939 NULL
/* instance */, -1 /* page_id */, kUrl1
, Referrer(),
940 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED
,
941 false /* is_renderer_init */);
942 host
= NavigateToEntry(manager
, entry1
);
944 // The RenderFrameHost created in Init will be reused.
945 EXPECT_TRUE(host
== manager
->current_frame_host());
946 EXPECT_FALSE(GetPendingFrameHost(manager
));
949 manager
->DidNavigateFrame(host
, true);
950 // Commit to SiteInstance should be delayed until RenderFrame commit.
951 EXPECT_TRUE(host
== manager
->current_frame_host());
953 EXPECT_FALSE(host
->GetSiteInstance()->HasSite());
954 host
->GetSiteInstance()->SetSite(kUrl1
);
956 // 2) Navigate to next site. -------------------------
957 const GURL
kUrl2("http://www.google.com/foo");
958 NavigationEntryImpl
entry2(
959 NULL
/* instance */, -1 /* page_id */, kUrl2
,
960 Referrer(kUrl1
, blink::WebReferrerPolicyDefault
),
961 base::string16() /* title */, ui::PAGE_TRANSITION_LINK
,
962 true /* is_renderer_init */);
963 host
= NavigateToEntry(manager
, entry2
);
965 // The RenderFrameHost created in Init will be reused.
966 EXPECT_TRUE(host
== manager
->current_frame_host());
967 EXPECT_FALSE(GetPendingFrameHost(manager
));
970 manager
->DidNavigateFrame(host
, true);
971 EXPECT_TRUE(host
== manager
->current_frame_host());
973 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
975 // 3) Cross-site navigate to next site. --------------
976 const GURL
kUrl3("http://webkit.org/");
977 NavigationEntryImpl
entry3(
978 NULL
/* instance */, -1 /* page_id */, kUrl3
,
979 Referrer(kUrl2
, blink::WebReferrerPolicyDefault
),
980 base::string16() /* title */, ui::PAGE_TRANSITION_LINK
,
981 false /* is_renderer_init */);
982 host
= NavigateToEntry(manager
, entry3
);
984 // A new RenderFrameHost should be created.
985 EXPECT_TRUE(GetPendingFrameHost(manager
));
986 ASSERT_EQ(host
, GetPendingFrameHost(manager
));
988 notifications
.Reset();
991 manager
->DidNavigateFrame(GetPendingFrameHost(manager
), true);
992 EXPECT_TRUE(host
== manager
->current_frame_host());
994 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
995 // Check the pending RenderFrameHost has been committed.
996 EXPECT_FALSE(GetPendingFrameHost(manager
));
998 // We should observe a notification.
1000 notifications
.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED
));
1003 // Tests WebUI creation.
1004 TEST_F(RenderFrameHostManagerTest
, WebUI
) {
1005 set_should_create_webui(true);
1006 SiteInstance
* instance
= SiteInstance::Create(browser_context());
1008 scoped_ptr
<TestWebContents
> web_contents(
1009 TestWebContents::Create(browser_context(), instance
));
1010 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
1011 RenderFrameHostImpl
* initial_rfh
= manager
->current_frame_host();
1013 EXPECT_FALSE(manager
->current_host()->IsRenderViewLive());
1014 EXPECT_FALSE(manager
->web_ui());
1015 EXPECT_TRUE(initial_rfh
);
1017 const GURL
kUrl("chrome://foo");
1018 NavigationEntryImpl
entry(NULL
/* instance */, -1 /* page_id */, kUrl
,
1019 Referrer(), base::string16() /* title */,
1020 ui::PAGE_TRANSITION_TYPED
,
1021 false /* is_renderer_init */);
1022 RenderFrameHostImpl
* host
= NavigateToEntry(manager
, entry
);
1024 // We commit the pending RenderFrameHost immediately because the previous
1025 // RenderFrameHost was not live. We test a case where it is live in
1028 EXPECT_NE(initial_rfh
, host
);
1029 EXPECT_EQ(host
, manager
->current_frame_host());
1030 EXPECT_FALSE(GetPendingFrameHost(manager
));
1032 // It's important that the SiteInstance get set on the Web UI page as soon
1033 // as the navigation starts, rather than lazily after it commits, so we don't
1034 // try to re-use the SiteInstance/process for non Web UI things that may
1035 // get loaded in between.
1036 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
1037 EXPECT_EQ(kUrl
, host
->GetSiteInstance()->GetSiteURL());
1039 // The Web UI is committed immediately because the RenderViewHost has not been
1040 // used yet. UpdateStateForNavigate() took the short cut path.
1041 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1042 switches::kEnableBrowserSideNavigation
)) {
1043 EXPECT_FALSE(manager
->speculative_web_ui());
1045 EXPECT_FALSE(manager
->pending_web_ui());
1047 EXPECT_TRUE(manager
->web_ui());
1050 manager
->DidNavigateFrame(host
, true);
1052 host
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1055 // Tests that we can open a WebUI link in a new tab from a WebUI page and still
1056 // grant the correct bindings. http://crbug.com/189101.
1057 TEST_F(RenderFrameHostManagerTest
, WebUIInNewTab
) {
1058 set_should_create_webui(true);
1059 SiteInstance
* blank_instance
= SiteInstance::Create(browser_context());
1061 // Create a blank tab.
1062 scoped_ptr
<TestWebContents
> web_contents1(
1063 TestWebContents::Create(browser_context(), blank_instance
));
1064 RenderFrameHostManager
* manager1
=
1065 web_contents1
->GetRenderManagerForTesting();
1066 // Test the case that new RVH is considered live.
1067 manager1
->current_host()->CreateRenderView(
1068 base::string16(), -1, MSG_ROUTING_NONE
, -1, false);
1069 EXPECT_TRUE(manager1
->current_host()->IsRenderViewLive());
1070 EXPECT_TRUE(manager1
->current_frame_host()->IsRenderFrameLive());
1072 // Navigate to a WebUI page.
1073 const GURL
kUrl1("chrome://foo");
1074 NavigationEntryImpl
entry1(NULL
/* instance */, -1 /* page_id */, kUrl1
,
1075 Referrer(), base::string16() /* title */,
1076 ui::PAGE_TRANSITION_TYPED
,
1077 false /* is_renderer_init */);
1078 RenderFrameHostImpl
* host1
= NavigateToEntry(manager1
, entry1
);
1080 // We should have a pending navigation to the WebUI RenderViewHost.
1081 // It should already have bindings.
1082 EXPECT_EQ(host1
, GetPendingFrameHost(manager1
));
1083 EXPECT_NE(host1
, manager1
->current_frame_host());
1085 host1
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1087 // Commit and ensure we still have bindings.
1088 manager1
->DidNavigateFrame(host1
, true);
1089 SiteInstance
* webui_instance
= host1
->GetSiteInstance();
1090 EXPECT_EQ(host1
, manager1
->current_frame_host());
1092 host1
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1094 // Now simulate clicking a link that opens in a new tab.
1095 scoped_ptr
<TestWebContents
> web_contents2(
1096 TestWebContents::Create(browser_context(), webui_instance
));
1097 RenderFrameHostManager
* manager2
=
1098 web_contents2
->GetRenderManagerForTesting();
1099 // Make sure the new RVH is considered live. This is usually done in
1100 // RenderWidgetHost::Init when opening a new tab from a link.
1101 manager2
->current_host()->CreateRenderView(
1102 base::string16(), -1, MSG_ROUTING_NONE
, -1, false);
1103 EXPECT_TRUE(manager2
->current_host()->IsRenderViewLive());
1105 const GURL
kUrl2("chrome://foo/bar");
1106 NavigationEntryImpl
entry2(NULL
/* instance */, -1 /* page_id */, kUrl2
,
1107 Referrer(), base::string16() /* title */,
1108 ui::PAGE_TRANSITION_LINK
,
1109 true /* is_renderer_init */);
1110 RenderFrameHostImpl
* host2
= NavigateToEntry(manager2
, entry2
);
1112 // No cross-process transition happens because we are already in the right
1113 // SiteInstance. We should grant bindings immediately.
1114 EXPECT_EQ(host2
, manager2
->current_frame_host());
1115 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1116 switches::kEnableBrowserSideNavigation
)) {
1117 EXPECT_TRUE(manager2
->speculative_web_ui());
1119 EXPECT_TRUE(manager2
->pending_web_ui());
1122 host2
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1124 manager2
->DidNavigateFrame(host2
, true);
1127 // Tests that a WebUI is correctly reused between chrome:// pages.
1128 TEST_F(RenderFrameHostManagerTest
, WebUIWasReused
) {
1129 set_should_create_webui(true);
1131 // Navigate to a WebUI page.
1132 const GURL
kUrl1("chrome://foo");
1133 contents()->NavigateAndCommit(kUrl1
);
1134 RenderFrameHostManager
* manager
=
1135 main_test_rfh()->frame_tree_node()->render_manager();
1136 WebUIImpl
* web_ui
= manager
->web_ui();
1137 EXPECT_TRUE(web_ui
);
1139 // Navigate to another WebUI page which should be same-site and keep the
1141 const GURL
kUrl2("chrome://foo/bar");
1142 contents()->NavigateAndCommit(kUrl2
);
1143 EXPECT_EQ(web_ui
, manager
->web_ui());
1146 // Tests that a WebUI is correctly cleaned up when navigating from a chrome://
1147 // page to a non-chrome:// page.
1148 TEST_F(RenderFrameHostManagerTest
, WebUIWasCleared
) {
1149 set_should_create_webui(true);
1151 // Navigate to a WebUI page.
1152 const GURL
kUrl1("chrome://foo");
1153 contents()->NavigateAndCommit(kUrl1
);
1154 EXPECT_TRUE(main_test_rfh()->frame_tree_node()->render_manager()->web_ui());
1156 // Navigate to a non-WebUI page.
1157 const GURL
kUrl2("http://www.google.com");
1158 contents()->NavigateAndCommit(kUrl2
);
1159 EXPECT_FALSE(main_test_rfh()->frame_tree_node()->render_manager()->web_ui());
1162 // Tests that we don't end up in an inconsistent state if a page does a back and
1163 // then reload. http://crbug.com/51680
1164 // Also tests that only user-gesture navigations can interrupt cross-process
1165 // navigations. http://crbug.com/75195
1166 TEST_F(RenderFrameHostManagerTest
, PageDoesBackAndReload
) {
1167 const GURL
kUrl1("http://www.google.com/");
1168 const GURL
kUrl2("http://www.evil-site.com/");
1170 // Navigate to a safe site, then an evil site.
1171 // This will switch RenderFrameHosts. We cannot assert that the first and
1172 // second RFHs are different, though, because the first one may be promptly
1174 contents()->NavigateAndCommit(kUrl1
);
1175 contents()->NavigateAndCommit(kUrl2
);
1176 TestRenderFrameHost
* evil_rfh
= contents()->GetMainFrame();
1178 // Now let's simulate the evil page calling history.back().
1179 contents()->OnGoToEntryAtOffset(-1);
1180 contents()->GetMainFrame()->PrepareForCommit();
1181 // We should have a new pending RFH.
1182 // Note that in this case, the navigation has not committed, so evil_rfh will
1183 // not be deleted yet.
1184 EXPECT_NE(evil_rfh
, contents()->GetPendingMainFrame());
1185 EXPECT_NE(evil_rfh
->GetRenderViewHost(),
1186 contents()->GetPendingMainFrame()->GetRenderViewHost());
1188 // Before that RFH has committed, the evil page reloads itself.
1189 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1192 params
.transition
= ui::PAGE_TRANSITION_CLIENT_REDIRECT
;
1193 params
.should_update_history
= false;
1194 params
.gesture
= NavigationGestureAuto
;
1195 params
.was_within_same_page
= false;
1196 params
.is_post
= false;
1197 params
.page_state
= PageState::CreateFromURL(kUrl2
);
1199 contents()->GetFrameTree()->root()->navigator()->DidNavigate(evil_rfh
,
1202 // That should NOT have cancelled the pending RFH, because the reload did
1203 // not have a user gesture. Thus, the pending back navigation will still
1204 // eventually commit.
1205 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
1206 pending_render_view_host() != NULL
);
1207 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->pending_frame_host() !=
1210 contents()->GetRenderManagerForTesting()->current_frame_host());
1211 EXPECT_EQ(evil_rfh
->GetRenderViewHost(),
1212 contents()->GetRenderManagerForTesting()->current_host());
1214 // Also we should not have a pending navigation entry.
1215 EXPECT_TRUE(contents()->GetController().GetPendingEntry() == NULL
);
1216 NavigationEntry
* entry
= contents()->GetController().GetVisibleEntry();
1217 ASSERT_TRUE(entry
!= NULL
);
1218 EXPECT_EQ(kUrl2
, entry
->GetURL());
1220 // Now do the same but as a user gesture.
1221 params
.gesture
= NavigationGestureUser
;
1222 contents()->GetFrameTree()->root()->navigator()->DidNavigate(evil_rfh
,
1225 // User navigation should have cancelled the pending RFH.
1226 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
1227 pending_render_view_host() == NULL
);
1228 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->pending_frame_host() ==
1231 contents()->GetRenderManagerForTesting()->current_frame_host());
1232 EXPECT_EQ(evil_rfh
->GetRenderViewHost(),
1233 contents()->GetRenderManagerForTesting()->current_host());
1235 // Also we should not have a pending navigation entry.
1236 EXPECT_TRUE(contents()->GetController().GetPendingEntry() == NULL
);
1237 entry
= contents()->GetController().GetVisibleEntry();
1238 ASSERT_TRUE(entry
!= NULL
);
1239 EXPECT_EQ(kUrl2
, entry
->GetURL());
1242 // Ensure that we can go back and forward even if a SwapOut ACK isn't received.
1243 // See http://crbug.com/93427.
1244 TEST_F(RenderFrameHostManagerTest
, NavigateAfterMissingSwapOutACK
) {
1245 const GURL
kUrl1("http://www.google.com/");
1246 const GURL
kUrl2("http://www.chromium.org/");
1248 // Navigate to two pages.
1249 contents()->NavigateAndCommit(kUrl1
);
1250 TestRenderFrameHost
* rfh1
= main_test_rfh();
1252 // Keep active_frame_count nonzero so that no swapped out frames in
1253 // this SiteInstance get forcefully deleted.
1254 rfh1
->GetSiteInstance()->increment_active_frame_count();
1256 contents()->NavigateAndCommit(kUrl2
);
1257 TestRenderFrameHost
* rfh2
= main_test_rfh();
1258 rfh2
->GetSiteInstance()->increment_active_frame_count();
1260 // Now go back, but suppose the SwapOut_ACK isn't received. This shouldn't
1261 // happen, but we have seen it when going back quickly across many entries
1262 // (http://crbug.com/93427).
1263 contents()->GetController().GoBack();
1264 EXPECT_TRUE(rfh2
->IsWaitingForBeforeUnloadACK());
1265 contents()->GetMainFrame()->PrepareForCommit();
1266 EXPECT_FALSE(rfh2
->IsWaitingForBeforeUnloadACK());
1268 // The back navigation commits.
1269 const NavigationEntry
* entry1
= contents()->GetController().GetPendingEntry();
1270 rfh1
->SendNavigate(entry1
->GetPageID(), entry1
->GetURL());
1271 EXPECT_TRUE(rfh2
->IsWaitingForUnloadACK());
1272 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh2
->rfh_state());
1274 // We should be able to navigate forward.
1275 contents()->GetController().GoForward();
1276 contents()->GetMainFrame()->PrepareForCommit();
1277 const NavigationEntry
* entry2
= contents()->GetController().GetPendingEntry();
1278 rfh2
->SendNavigate(entry2
->GetPageID(), entry2
->GetURL());
1279 EXPECT_EQ(rfh2
, main_test_rfh());
1280 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
1281 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
1282 rfh1
->OnSwappedOut();
1283 EXPECT_TRUE(rfh1
->is_swapped_out());
1284 EXPECT_EQ(RenderFrameHostImpl::STATE_SWAPPED_OUT
, rfh1
->rfh_state());
1287 // Test that we create swapped out RFHs for the opener chain when navigating an
1288 // opened tab cross-process. This allows us to support certain cross-process
1289 // JavaScript calls (http://crbug.com/99202).
1290 TEST_F(RenderFrameHostManagerTest
, CreateSwappedOutOpenerRFHs
) {
1291 const GURL
kUrl1("http://www.google.com/");
1292 const GURL
kUrl2("http://www.chromium.org/");
1293 const GURL
kChromeUrl("chrome://foo");
1295 // Navigate to an initial URL.
1296 contents()->NavigateAndCommit(kUrl1
);
1297 RenderFrameHostManager
* manager
= contents()->GetRenderManagerForTesting();
1298 TestRenderFrameHost
* rfh1
= main_test_rfh();
1299 TestRenderViewHost
* rvh1
= test_rvh();
1301 // Create 2 new tabs and simulate them being the opener chain for the main
1302 // tab. They should be in the same SiteInstance.
1303 scoped_ptr
<TestWebContents
> opener1(
1304 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1305 RenderFrameHostManager
* opener1_manager
=
1306 opener1
->GetRenderManagerForTesting();
1307 contents()->SetOpener(opener1
.get());
1309 scoped_ptr
<TestWebContents
> opener2(
1310 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1311 RenderFrameHostManager
* opener2_manager
=
1312 opener2
->GetRenderManagerForTesting();
1313 opener1
->SetOpener(opener2
.get());
1315 // Navigate to a cross-site URL (different SiteInstance but same
1316 // BrowsingInstance).
1317 contents()->NavigateAndCommit(kUrl2
);
1318 TestRenderFrameHost
* rfh2
= main_test_rfh();
1319 TestRenderViewHost
* rvh2
= test_rvh();
1320 EXPECT_NE(rfh1
->GetSiteInstance(), rfh2
->GetSiteInstance());
1321 EXPECT_TRUE(rfh1
->GetSiteInstance()->IsRelatedSiteInstance(
1322 rfh2
->GetSiteInstance()));
1324 // Ensure rvh1 is placed on swapped out list of the current tab.
1325 EXPECT_TRUE(manager
->IsOnSwappedOutList(rfh1
));
1326 EXPECT_TRUE(manager
->IsRVHOnSwappedOutList(rvh1
));
1328 manager
->GetRenderFrameProxyHost(rfh1
->GetSiteInstance())
1329 ->render_frame_host());
1331 manager
->GetSwappedOutRenderViewHost(rvh1
->GetSiteInstance()));
1333 // Ensure a swapped out RFH and RFH is created in the first opener tab.
1334 RenderFrameProxyHost
* opener1_proxy
=
1335 opener1_manager
->GetRenderFrameProxyHost(rfh2
->GetSiteInstance());
1336 RenderFrameHostImpl
* opener1_rfh
= opener1_proxy
->render_frame_host();
1337 TestRenderViewHost
* opener1_rvh
= static_cast<TestRenderViewHost
*>(
1338 opener1_manager
->GetSwappedOutRenderViewHost(rvh2
->GetSiteInstance()));
1339 EXPECT_TRUE(opener1_manager
->IsOnSwappedOutList(opener1_rfh
));
1340 EXPECT_TRUE(opener1_manager
->IsRVHOnSwappedOutList(opener1_rvh
));
1341 EXPECT_TRUE(opener1_rfh
->is_swapped_out());
1342 EXPECT_FALSE(opener1_rvh
->is_active());
1344 // Ensure a swapped out RFH and RVH is created in the second opener tab.
1345 RenderFrameProxyHost
* opener2_proxy
=
1346 opener2_manager
->GetRenderFrameProxyHost(rfh2
->GetSiteInstance());
1347 RenderFrameHostImpl
* opener2_rfh
= opener2_proxy
->render_frame_host();
1348 TestRenderViewHost
* opener2_rvh
= static_cast<TestRenderViewHost
*>(
1349 opener2_manager
->GetSwappedOutRenderViewHost(rvh2
->GetSiteInstance()));
1350 EXPECT_TRUE(opener2_manager
->IsOnSwappedOutList(opener2_rfh
));
1351 EXPECT_TRUE(opener2_manager
->IsRVHOnSwappedOutList(opener2_rvh
));
1352 EXPECT_TRUE(opener2_rfh
->is_swapped_out());
1353 EXPECT_FALSE(opener2_rvh
->is_active());
1355 // Navigate to a cross-BrowsingInstance URL.
1356 contents()->NavigateAndCommit(kChromeUrl
);
1357 TestRenderFrameHost
* rfh3
= main_test_rfh();
1358 EXPECT_NE(rfh1
->GetSiteInstance(), rfh3
->GetSiteInstance());
1359 EXPECT_FALSE(rfh1
->GetSiteInstance()->IsRelatedSiteInstance(
1360 rfh3
->GetSiteInstance()));
1362 // No scripting is allowed across BrowsingInstances, so we should not create
1363 // swapped out RVHs for the opener chain in this case.
1364 EXPECT_FALSE(opener1_manager
->GetRenderFrameProxyHost(
1365 rfh3
->GetSiteInstance()));
1366 EXPECT_FALSE(opener1_manager
->GetSwappedOutRenderViewHost(
1367 rfh3
->GetSiteInstance()));
1368 EXPECT_FALSE(opener2_manager
->GetRenderFrameProxyHost(
1369 rfh3
->GetSiteInstance()));
1370 EXPECT_FALSE(opener2_manager
->GetSwappedOutRenderViewHost(
1371 rfh3
->GetSiteInstance()));
1374 // Test that a page can disown the opener of the WebContents.
1375 TEST_F(RenderFrameHostManagerTest
, DisownOpener
) {
1376 const GURL
kUrl1("http://www.google.com/");
1377 const GURL
kUrl2("http://www.chromium.org/");
1379 // Navigate to an initial URL.
1380 contents()->NavigateAndCommit(kUrl1
);
1381 TestRenderFrameHost
* rfh1
= main_test_rfh();
1383 // Create a new tab and simulate having it be the opener for the main tab.
1384 scoped_ptr
<TestWebContents
> opener1(
1385 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1386 contents()->SetOpener(opener1
.get());
1387 EXPECT_TRUE(contents()->HasOpener());
1389 // Navigate to a cross-site URL (different SiteInstance but same
1390 // BrowsingInstance).
1391 contents()->NavigateAndCommit(kUrl2
);
1392 TestRenderFrameHost
* rfh2
= main_test_rfh();
1393 EXPECT_NE(rfh1
->GetSiteInstance(), rfh2
->GetSiteInstance());
1395 // Disown the opener from rfh2.
1396 rfh2
->DidDisownOpener();
1398 // Ensure the opener is cleared.
1399 EXPECT_FALSE(contents()->HasOpener());
1402 // Test that a page can disown a same-site opener of the WebContents.
1403 TEST_F(RenderFrameHostManagerTest
, DisownSameSiteOpener
) {
1404 const GURL
kUrl1("http://www.google.com/");
1406 // Navigate to an initial URL.
1407 contents()->NavigateAndCommit(kUrl1
);
1408 TestRenderFrameHost
* rfh1
= main_test_rfh();
1410 // Create a new tab and simulate having it be the opener for the main tab.
1411 scoped_ptr
<TestWebContents
> opener1(
1412 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1413 contents()->SetOpener(opener1
.get());
1414 EXPECT_TRUE(contents()->HasOpener());
1416 // Disown the opener from rfh1.
1417 rfh1
->DidDisownOpener();
1419 // Ensure the opener is cleared even if it is in the same process.
1420 EXPECT_FALSE(contents()->HasOpener());
1423 // Test that a page can disown the opener just as a cross-process navigation is
1425 TEST_F(RenderFrameHostManagerTest
, DisownOpenerDuringNavigation
) {
1426 const GURL
kUrl1("http://www.google.com/");
1427 const GURL
kUrl2("http://www.chromium.org/");
1429 // Navigate to an initial URL.
1430 contents()->NavigateAndCommit(kUrl1
);
1431 TestRenderFrameHost
* rfh1
= main_test_rfh();
1433 // Create a new tab and simulate having it be the opener for the main tab.
1434 scoped_ptr
<TestWebContents
> opener1(
1435 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1436 contents()->SetOpener(opener1
.get());
1437 EXPECT_TRUE(contents()->HasOpener());
1439 // Navigate to a cross-site URL (different SiteInstance but same
1440 // BrowsingInstance).
1441 contents()->NavigateAndCommit(kUrl2
);
1442 TestRenderFrameHost
* rfh2
= main_test_rfh();
1443 EXPECT_NE(rfh1
->GetSiteInstance(), rfh2
->GetSiteInstance());
1445 // Start a back navigation so that rfh1 becomes the pending RFH.
1446 contents()->GetController().GoBack();
1447 contents()->GetMainFrame()->PrepareForCommit();
1449 // Disown the opener from rfh2.
1450 rfh2
->DidDisownOpener();
1452 // Ensure the opener is cleared.
1453 EXPECT_FALSE(contents()->HasOpener());
1455 // The back navigation commits.
1456 const NavigationEntry
* entry1
= contents()->GetController().GetPendingEntry();
1457 rfh1
->SendNavigate(entry1
->GetPageID(), entry1
->GetURL());
1459 // Ensure the opener is still cleared.
1460 EXPECT_FALSE(contents()->HasOpener());
1463 // Test that a page can disown the opener just after a cross-process navigation
1465 TEST_F(RenderFrameHostManagerTest
, DisownOpenerAfterNavigation
) {
1466 const GURL
kUrl1("http://www.google.com/");
1467 const GURL
kUrl2("http://www.chromium.org/");
1469 // Navigate to an initial URL.
1470 contents()->NavigateAndCommit(kUrl1
);
1471 TestRenderFrameHost
* rfh1
= main_test_rfh();
1473 // Create a new tab and simulate having it be the opener for the main tab.
1474 scoped_ptr
<TestWebContents
> opener1(
1475 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1476 contents()->SetOpener(opener1
.get());
1477 EXPECT_TRUE(contents()->HasOpener());
1479 // Navigate to a cross-site URL (different SiteInstance but same
1480 // BrowsingInstance).
1481 contents()->NavigateAndCommit(kUrl2
);
1482 TestRenderFrameHost
* rfh2
= main_test_rfh();
1483 EXPECT_NE(rfh1
->GetSiteInstance(), rfh2
->GetSiteInstance());
1485 // Commit a back navigation before the DidDisownOpener message arrives.
1486 // rfh1 will be kept alive because of the opener tab.
1487 contents()->GetController().GoBack();
1488 contents()->GetMainFrame()->PrepareForCommit();
1489 const NavigationEntry
* entry1
= contents()->GetController().GetPendingEntry();
1490 rfh1
->SendNavigate(entry1
->GetPageID(), entry1
->GetURL());
1492 // Disown the opener from rfh2.
1493 rfh2
->DidDisownOpener();
1494 EXPECT_FALSE(contents()->HasOpener());
1497 // Test that we clean up swapped out RenderViewHosts when a process hosting
1498 // those associated RenderViews crashes. http://crbug.com/258993
1499 TEST_F(RenderFrameHostManagerTest
, CleanUpSwappedOutRVHOnProcessCrash
) {
1500 const GURL
kUrl1("http://www.google.com/");
1501 const GURL
kUrl2("http://www.chromium.org/");
1503 // Navigate to an initial URL.
1504 contents()->NavigateAndCommit(kUrl1
);
1505 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1507 // Create a new tab as an opener for the main tab.
1508 scoped_ptr
<TestWebContents
> opener1(
1509 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1510 RenderFrameHostManager
* opener1_manager
=
1511 opener1
->GetRenderManagerForTesting();
1512 contents()->SetOpener(opener1
.get());
1514 // Make sure the new opener RVH is considered live.
1515 opener1_manager
->current_host()->CreateRenderView(
1516 base::string16(), -1, MSG_ROUTING_NONE
, -1, false);
1517 EXPECT_TRUE(opener1_manager
->current_host()->IsRenderViewLive());
1518 EXPECT_TRUE(opener1_manager
->current_frame_host()->IsRenderFrameLive());
1520 // Use a cross-process navigation in the opener to swap out the old RVH.
1522 opener1_manager
->GetSwappedOutRenderViewHost(rfh1
->GetSiteInstance()));
1523 opener1
->NavigateAndCommit(kUrl2
);
1525 opener1_manager
->GetSwappedOutRenderViewHost(rfh1
->GetSiteInstance()));
1527 // Fake a process crash.
1528 rfh1
->GetProcess()->SimulateCrash();
1530 // Ensure that the RenderFrameProxyHost stays around and the RenderFrameProxy
1532 RenderFrameProxyHost
* render_frame_proxy_host
=
1533 opener1_manager
->GetRenderFrameProxyHost(rfh1
->GetSiteInstance());
1534 EXPECT_TRUE(render_frame_proxy_host
);
1535 EXPECT_FALSE(render_frame_proxy_host
->is_render_frame_proxy_live());
1537 // Expect the swapped out RVH to exist.
1539 opener1_manager
->GetSwappedOutRenderViewHost(rfh1
->GetSiteInstance()));
1541 // Reload the initial tab. This should recreate the opener's swapped out RVH
1542 // in the original SiteInstance.
1543 contents()->GetController().Reload(true);
1544 contents()->GetMainFrame()->PrepareForCommit();
1545 EXPECT_EQ(opener1_manager
->GetSwappedOutRenderViewHost(
1546 rfh1
->GetSiteInstance())->GetRoutingID(),
1547 contents()->GetMainFrame()->GetRenderViewHost()->opener_route_id());
1550 // Test that RenderViewHosts created for WebUI navigations are properly
1551 // granted WebUI bindings even if an unprivileged swapped out RenderViewHost
1552 // is in the same process (http://crbug.com/79918).
1553 TEST_F(RenderFrameHostManagerTest
, EnableWebUIWithSwappedOutOpener
) {
1554 set_should_create_webui(true);
1555 const GURL
kSettingsUrl("chrome://chrome/settings");
1556 const GURL
kPluginUrl("chrome://plugins");
1558 // Navigate to an initial WebUI URL.
1559 contents()->NavigateAndCommit(kSettingsUrl
);
1561 // Ensure the RVH has WebUI bindings.
1562 TestRenderViewHost
* rvh1
= test_rvh();
1563 EXPECT_TRUE(rvh1
->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1565 // Create a new tab and simulate it being the opener for the main
1566 // tab. It should be in the same SiteInstance.
1567 scoped_ptr
<TestWebContents
> opener1(
1568 TestWebContents::Create(browser_context(), rvh1
->GetSiteInstance()));
1569 RenderFrameHostManager
* opener1_manager
=
1570 opener1
->GetRenderManagerForTesting();
1571 contents()->SetOpener(opener1
.get());
1573 // Navigate to a different WebUI URL (different SiteInstance, same
1574 // BrowsingInstance).
1575 contents()->NavigateAndCommit(kPluginUrl
);
1576 TestRenderViewHost
* rvh2
= test_rvh();
1577 EXPECT_NE(rvh1
->GetSiteInstance(), rvh2
->GetSiteInstance());
1578 EXPECT_TRUE(rvh1
->GetSiteInstance()->IsRelatedSiteInstance(
1579 rvh2
->GetSiteInstance()));
1581 // Ensure a swapped out RFH and RVH is created in the first opener tab.
1582 RenderFrameProxyHost
* opener1_proxy
=
1583 opener1_manager
->GetRenderFrameProxyHost(rvh2
->GetSiteInstance());
1584 RenderFrameHostImpl
* opener1_rfh
= opener1_proxy
->render_frame_host();
1585 TestRenderViewHost
* opener1_rvh
= static_cast<TestRenderViewHost
*>(
1586 opener1_manager
->GetSwappedOutRenderViewHost(rvh2
->GetSiteInstance()));
1587 EXPECT_TRUE(opener1_manager
->IsOnSwappedOutList(opener1_rfh
));
1588 EXPECT_TRUE(opener1_manager
->IsRVHOnSwappedOutList(opener1_rvh
));
1589 EXPECT_TRUE(opener1_rfh
->is_swapped_out());
1590 EXPECT_FALSE(opener1_rvh
->is_active());
1592 // Ensure the new RVH has WebUI bindings.
1593 EXPECT_TRUE(rvh2
->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1596 // Test that we reuse the same guest SiteInstance if we navigate across sites.
1597 TEST_F(RenderFrameHostManagerTest
, NoSwapOnGuestNavigations
) {
1598 TestNotificationTracker notifications
;
1600 GURL
guest_url(std::string(kGuestScheme
).append("://abc123"));
1601 SiteInstance
* instance
=
1602 SiteInstance::CreateForURL(browser_context(), guest_url
);
1603 scoped_ptr
<TestWebContents
> web_contents(
1604 TestWebContents::Create(browser_context(), instance
));
1606 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
1608 RenderFrameHostImpl
* host
= NULL
;
1610 // 1) The first navigation. --------------------------
1611 const GURL
kUrl1("http://www.google.com/");
1612 NavigationEntryImpl
entry1(
1613 NULL
/* instance */, -1 /* page_id */, kUrl1
, Referrer(),
1614 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED
,
1615 false /* is_renderer_init */);
1616 host
= NavigateToEntry(manager
, entry1
);
1618 // The RenderFrameHost created in Init will be reused.
1619 EXPECT_TRUE(host
== manager
->current_frame_host());
1620 EXPECT_FALSE(manager
->pending_frame_host());
1621 EXPECT_EQ(manager
->current_frame_host()->GetSiteInstance(), instance
);
1624 manager
->DidNavigateFrame(host
, true);
1625 // Commit to SiteInstance should be delayed until RenderFrame commit.
1626 EXPECT_EQ(host
, manager
->current_frame_host());
1628 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
1630 // 2) Navigate to a different domain. -------------------------
1631 // Guests stay in the same process on navigation.
1632 const GURL
kUrl2("http://www.chromium.org");
1633 NavigationEntryImpl
entry2(
1634 NULL
/* instance */, -1 /* page_id */, kUrl2
,
1635 Referrer(kUrl1
, blink::WebReferrerPolicyDefault
),
1636 base::string16() /* title */, ui::PAGE_TRANSITION_LINK
,
1637 true /* is_renderer_init */);
1638 host
= NavigateToEntry(manager
, entry2
);
1640 // The RenderFrameHost created in Init will be reused.
1641 EXPECT_EQ(host
, manager
->current_frame_host());
1642 EXPECT_FALSE(manager
->pending_frame_host());
1645 manager
->DidNavigateFrame(host
, true);
1646 EXPECT_EQ(host
, manager
->current_frame_host());
1648 EXPECT_EQ(host
->GetSiteInstance(), instance
);
1651 // Test that we cancel a pending RVH if we close the tab while it's pending.
1652 // http://crbug.com/294697.
1653 TEST_F(RenderFrameHostManagerTest
, NavigateWithEarlyClose
) {
1654 TestNotificationTracker notifications
;
1656 SiteInstance
* instance
= SiteInstance::Create(browser_context());
1658 BeforeUnloadFiredWebContentsDelegate delegate
;
1659 scoped_ptr
<TestWebContents
> web_contents(
1660 TestWebContents::Create(browser_context(), instance
));
1661 web_contents
->SetDelegate(&delegate
);
1662 notifications
.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED
,
1663 Source
<WebContents
>(web_contents
.get()));
1665 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
1667 // 1) The first navigation. --------------------------
1668 const GURL
kUrl1("http://www.google.com/");
1669 NavigationEntryImpl
entry1(NULL
/* instance */, -1 /* page_id */, kUrl1
,
1670 Referrer(), base::string16() /* title */,
1671 ui::PAGE_TRANSITION_TYPED
,
1672 false /* is_renderer_init */);
1673 RenderFrameHostImpl
* host
= NavigateToEntry(manager
, entry1
);
1675 // The RenderFrameHost created in Init will be reused.
1676 EXPECT_EQ(host
, manager
->current_frame_host());
1677 EXPECT_FALSE(GetPendingFrameHost(manager
));
1679 // We should observe a notification.
1681 notifications
.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED
));
1682 notifications
.Reset();
1685 manager
->DidNavigateFrame(host
, true);
1687 // Commit to SiteInstance should be delayed until RenderFrame commits.
1688 EXPECT_EQ(host
, manager
->current_frame_host());
1689 EXPECT_FALSE(host
->GetSiteInstance()->HasSite());
1690 host
->GetSiteInstance()->SetSite(kUrl1
);
1692 // 2) Cross-site navigate to next site. -------------------------
1693 const GURL
kUrl2("http://www.example.com");
1694 NavigationEntryImpl
entry2(
1695 NULL
/* instance */, -1 /* page_id */, kUrl2
, Referrer(),
1696 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED
,
1697 false /* is_renderer_init */);
1698 RenderFrameHostImpl
* host2
= NavigateToEntry(manager
, entry2
);
1700 // A new RenderFrameHost should be created.
1701 ASSERT_EQ(host2
, GetPendingFrameHost(manager
));
1702 EXPECT_NE(host2
, host
);
1704 EXPECT_EQ(host
, manager
->current_frame_host());
1705 EXPECT_FALSE(manager
->current_frame_host()->is_swapped_out());
1706 EXPECT_EQ(host2
, GetPendingFrameHost(manager
));
1708 // 3) Close the tab. -------------------------
1709 notifications
.ListenFor(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED
,
1710 Source
<RenderWidgetHost
>(host2
->render_view_host()));
1711 manager
->OnBeforeUnloadACK(false, true, base::TimeTicks());
1714 notifications
.Check1AndReset(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED
));
1715 EXPECT_FALSE(GetPendingFrameHost(manager
));
1716 EXPECT_EQ(host
, manager
->current_frame_host());
1719 TEST_F(RenderFrameHostManagerTest
, CloseWithPendingWhileUnresponsive
) {
1720 const GURL
kUrl1("http://www.google.com/");
1721 const GURL
kUrl2("http://www.chromium.org/");
1723 CloseWebContentsDelegate close_delegate
;
1724 contents()->SetDelegate(&close_delegate
);
1726 // Navigate to the first page.
1727 contents()->NavigateAndCommit(kUrl1
);
1728 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1730 // Start to close the tab, but assume it's unresponsive.
1731 rfh1
->render_view_host()->ClosePage();
1732 EXPECT_TRUE(rfh1
->render_view_host()->is_waiting_for_close_ack());
1734 // Start a navigation to a new site.
1735 controller().LoadURL(
1736 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1737 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1738 switches::kEnableBrowserSideNavigation
)) {
1739 rfh1
->PrepareForCommit();
1741 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1743 // Simulate the unresponsiveness timer. The tab should close.
1744 contents()->RendererUnresponsive(rfh1
->render_view_host());
1745 EXPECT_TRUE(close_delegate
.is_closed());
1748 // Tests that the RenderFrameHost is properly deleted when the SwapOutACK is
1749 // received. (SwapOut and the corresponding ACK always occur after commit.)
1750 // Also tests that an early SwapOutACK is properly ignored.
1751 TEST_F(RenderFrameHostManagerTest
, DeleteFrameAfterSwapOutACK
) {
1752 const GURL
kUrl1("http://www.google.com/");
1753 const GURL
kUrl2("http://www.chromium.org/");
1755 // Navigate to the first page.
1756 contents()->NavigateAndCommit(kUrl1
);
1757 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1758 RenderFrameHostDeletedObserver
rfh_deleted_observer(rfh1
);
1759 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1761 // Navigate to new site, simulating onbeforeunload approval.
1762 controller().LoadURL(
1763 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1764 contents()->GetMainFrame()->PrepareForCommit();
1765 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1766 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1767 TestRenderFrameHost
* rfh2
= contents()->GetPendingMainFrame();
1769 // Simulate the swap out ack, unexpectedly early (before commit). It should
1771 rfh1
->OnSwappedOut();
1772 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1773 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1775 // The new page commits.
1776 contents()->TestDidNavigate(rfh2
, 1, kUrl2
, ui::PAGE_TRANSITION_TYPED
);
1777 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1778 EXPECT_EQ(rfh2
, contents()->GetMainFrame());
1779 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL
);
1780 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
1781 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
1783 rfh1
->frame_tree_node()->render_manager()->IsPendingDeletion(rfh1
));
1785 // Simulate the swap out ack.
1786 rfh1
->OnSwappedOut();
1788 // rfh1 should have been deleted.
1789 EXPECT_TRUE(rfh_deleted_observer
.deleted());
1793 // Tests that the RenderFrameHost is properly swapped out when the SwapOut ACK
1794 // is received. (SwapOut and the corresponding ACK always occur after commit.)
1795 TEST_F(RenderFrameHostManagerTest
, SwapOutFrameAfterSwapOutACK
) {
1796 const GURL
kUrl1("http://www.google.com/");
1797 const GURL
kUrl2("http://www.chromium.org/");
1799 // Navigate to the first page.
1800 contents()->NavigateAndCommit(kUrl1
);
1801 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1802 RenderFrameHostDeletedObserver
rfh_deleted_observer(rfh1
);
1803 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1805 // Increment the number of active frames in SiteInstanceImpl so that rfh1 is
1806 // not deleted on swap out.
1807 rfh1
->GetSiteInstance()->increment_active_frame_count();
1809 // Navigate to new site, simulating onbeforeunload approval.
1810 controller().LoadURL(
1811 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1812 contents()->GetMainFrame()->PrepareForCommit();
1813 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1814 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1815 TestRenderFrameHost
* rfh2
= contents()->GetPendingMainFrame();
1817 // The new page commits.
1818 contents()->TestDidNavigate(rfh2
, 1, kUrl2
, ui::PAGE_TRANSITION_TYPED
);
1819 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1820 EXPECT_EQ(rfh2
, contents()->GetMainFrame());
1821 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL
);
1822 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
1823 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
1825 // Simulate the swap out ack.
1826 rfh1
->OnSwappedOut();
1828 // rfh1 should be swapped out.
1829 EXPECT_FALSE(rfh_deleted_observer
.deleted());
1830 EXPECT_TRUE(rfh1
->is_swapped_out());
1833 // Test that the RenderViewHost is properly swapped out if a navigation in the
1834 // new renderer commits before sending the SwapOut message to the old renderer.
1835 // This simulates a cross-site navigation to a synchronously committing URL
1836 // (e.g., a data URL) and ensures it works properly.
1837 TEST_F(RenderFrameHostManagerTest
,
1838 CommitNewNavigationBeforeSendingSwapOut
) {
1839 const GURL
kUrl1("http://www.google.com/");
1840 const GURL
kUrl2("http://www.chromium.org/");
1842 // Navigate to the first page.
1843 contents()->NavigateAndCommit(kUrl1
);
1844 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1845 RenderFrameHostDeletedObserver
rfh_deleted_observer(rfh1
);
1846 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1848 // Increment the number of active frames in SiteInstanceImpl so that rfh1 is
1849 // not deleted on swap out.
1850 rfh1
->GetSiteInstance()->increment_active_frame_count();
1852 // Navigate to new site, simulating onbeforeunload approval.
1853 controller().LoadURL(
1854 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1855 rfh1
->PrepareForCommit();
1856 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1857 TestRenderFrameHost
* rfh2
= contents()->GetPendingMainFrame();
1859 // The new page commits.
1860 contents()->TestDidNavigate(rfh2
, 1, kUrl2
, ui::PAGE_TRANSITION_TYPED
);
1861 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1862 EXPECT_EQ(rfh2
, contents()->GetMainFrame());
1863 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL
);
1864 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
1865 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
1867 // Simulate the swap out ack.
1868 rfh1
->OnSwappedOut();
1870 // rfh1 should be swapped out.
1871 EXPECT_FALSE(rfh_deleted_observer
.deleted());
1872 EXPECT_TRUE(rfh1
->is_swapped_out());
1875 // Test that a RenderFrameHost is properly deleted or swapped out when a
1876 // cross-site navigation is cancelled.
1877 TEST_F(RenderFrameHostManagerTest
,
1878 CancelPendingProperlyDeletesOrSwaps
) {
1879 const GURL
kUrl1("http://www.google.com/");
1880 const GURL
kUrl2("http://www.chromium.org/");
1881 RenderFrameHostImpl
* pending_rfh
= NULL
;
1882 base::TimeTicks now
= base::TimeTicks::Now();
1884 // Navigate to the first page.
1885 contents()->NavigateAndCommit(kUrl1
);
1886 TestRenderFrameHost
* rfh1
= main_test_rfh();
1887 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1889 // Navigate to a new site, starting a cross-site navigation.
1890 controller().LoadURL(
1891 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1893 pending_rfh
= contents()->GetFrameTree()->root()->render_manager()
1894 ->pending_frame_host();
1895 RenderFrameHostDeletedObserver
rfh_deleted_observer(pending_rfh
);
1897 // Cancel the navigation by simulating a declined beforeunload dialog.
1898 contents()->GetMainFrame()->OnMessageReceived(
1899 FrameHostMsg_BeforeUnload_ACK(0, false, now
, now
));
1900 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1902 // Since the pending RFH is the only one for the new SiteInstance, it should
1904 EXPECT_TRUE(rfh_deleted_observer
.deleted());
1907 // Start another cross-site navigation.
1908 controller().LoadURL(
1909 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1911 pending_rfh
= contents()->GetFrameTree()->root()->render_manager()
1912 ->pending_frame_host();
1913 RenderFrameHostDeletedObserver
rfh_deleted_observer(pending_rfh
);
1915 // Increment the number of active frames in the new SiteInstance, which will
1916 // cause the pending RFH to be swapped out instead of deleted.
1917 pending_rfh
->GetSiteInstance()->increment_active_frame_count();
1919 contents()->GetMainFrame()->OnMessageReceived(
1920 FrameHostMsg_BeforeUnload_ACK(0, false, now
, now
));
1921 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1922 EXPECT_FALSE(rfh_deleted_observer
.deleted());
1926 // Test that a pending RenderFrameHost in a non-root frame tree node is properly
1927 // deleted when the node is detached. Motivated by http://crbug.com/441357 and
1928 // http://crbug.com/444955.
1929 TEST_F(RenderFrameHostManagerTest
, DetachPendingChild
) {
1930 base::CommandLine::ForCurrentProcess()->AppendSwitch(
1931 switches::kSitePerProcess
);
1933 const GURL
kUrlA("http://www.google.com/");
1934 const GURL
kUrlB("http://webkit.org/");
1936 // Create a page with two child frames.
1937 contents()->NavigateAndCommit(kUrlA
);
1938 contents()->GetMainFrame()->OnCreateChildFrame(
1939 contents()->GetMainFrame()->GetProcess()->GetNextRoutingID(),
1940 std::string("frame_name"), SandboxFlags::NONE
);
1941 contents()->GetMainFrame()->OnCreateChildFrame(
1942 contents()->GetMainFrame()->GetProcess()->GetNextRoutingID(),
1943 std::string("frame_name"), SandboxFlags::NONE
);
1944 RenderFrameHostManager
* root_manager
=
1945 contents()->GetFrameTree()->root()->render_manager();
1946 RenderFrameHostManager
* iframe1
=
1947 contents()->GetFrameTree()->root()->child_at(0)->render_manager();
1948 RenderFrameHostManager
* iframe2
=
1949 contents()->GetFrameTree()->root()->child_at(1)->render_manager();
1951 // 1) The first navigation.
1952 NavigationEntryImpl
entryA(NULL
/* instance */, -1 /* page_id */, kUrlA
,
1953 Referrer(), base::string16() /* title */,
1954 ui::PAGE_TRANSITION_TYPED
,
1955 false /* is_renderer_init */);
1956 RenderFrameHostImpl
* host1
= NavigateToEntry(iframe1
, entryA
);
1958 // The RenderFrameHost created in Init will be reused.
1959 EXPECT_TRUE(host1
== iframe1
->current_frame_host());
1960 EXPECT_FALSE(GetPendingFrameHost(iframe1
));
1963 iframe1
->DidNavigateFrame(host1
, true);
1964 // Commit to SiteInstance should be delayed until RenderFrame commit.
1965 EXPECT_TRUE(host1
== iframe1
->current_frame_host());
1967 EXPECT_TRUE(host1
->GetSiteInstance()->HasSite());
1969 // 2) Cross-site navigate both frames to next site.
1970 NavigationEntryImpl
entryB(NULL
/* instance */, -1 /* page_id */, kUrlB
,
1971 Referrer(kUrlA
, blink::WebReferrerPolicyDefault
),
1972 base::string16() /* title */,
1973 ui::PAGE_TRANSITION_LINK
,
1974 false /* is_renderer_init */);
1975 host1
= NavigateToEntry(iframe1
, entryB
);
1976 RenderFrameHostImpl
* host2
= NavigateToEntry(iframe2
, entryB
);
1978 // A new, pending RenderFrameHost should be created in each FrameTreeNode.
1979 EXPECT_TRUE(GetPendingFrameHost(iframe1
));
1980 EXPECT_TRUE(GetPendingFrameHost(iframe2
));
1981 EXPECT_EQ(host1
, GetPendingFrameHost(iframe1
));
1982 EXPECT_EQ(host2
, GetPendingFrameHost(iframe2
));
1983 EXPECT_TRUE(RenderFrameHostImpl::IsRFHStateActive(
1984 GetPendingFrameHost(iframe1
)->rfh_state()));
1985 EXPECT_TRUE(RenderFrameHostImpl::IsRFHStateActive(
1986 GetPendingFrameHost(iframe2
)->rfh_state()));
1987 EXPECT_NE(GetPendingFrameHost(iframe1
), GetPendingFrameHost(iframe2
));
1988 EXPECT_EQ(GetPendingFrameHost(iframe1
)->GetSiteInstance(),
1989 GetPendingFrameHost(iframe2
)->GetSiteInstance());
1990 EXPECT_NE(iframe1
->current_frame_host(), GetPendingFrameHost(iframe1
));
1991 EXPECT_NE(iframe2
->current_frame_host(), GetPendingFrameHost(iframe2
));
1992 EXPECT_FALSE(contents()->CrossProcessNavigationPending())
1993 << "There should be no top-level pending navigation.";
1995 RenderFrameHostDeletedObserver
delete_watcher1(GetPendingFrameHost(iframe1
));
1996 RenderFrameHostDeletedObserver
delete_watcher2(GetPendingFrameHost(iframe2
));
1997 EXPECT_FALSE(delete_watcher1
.deleted());
1998 EXPECT_FALSE(delete_watcher2
.deleted());
2000 // Keep the SiteInstance alive for testing.
2001 scoped_refptr
<SiteInstanceImpl
> site_instance
=
2002 GetPendingFrameHost(iframe1
)->GetSiteInstance();
2003 EXPECT_TRUE(site_instance
->HasSite());
2004 EXPECT_NE(site_instance
, contents()->GetSiteInstance());
2005 EXPECT_EQ(2U, site_instance
->active_frame_count());
2007 // Proxies should exist.
2009 root_manager
->GetRenderFrameProxyHost(site_instance
.get()));
2011 iframe1
->GetRenderFrameProxyHost(site_instance
.get()));
2013 iframe2
->GetRenderFrameProxyHost(site_instance
.get()));
2015 // Detach the first child FrameTreeNode. This should kill the pending host but
2016 // not yet destroy proxies in |site_instance| since the other child remains.
2017 iframe1
->current_frame_host()->OnMessageReceived(
2018 FrameHostMsg_Detach(iframe1
->current_frame_host()->GetRoutingID()));
2019 iframe1
= NULL
; // Was just destroyed.
2021 EXPECT_TRUE(delete_watcher1
.deleted());
2022 EXPECT_FALSE(delete_watcher2
.deleted());
2023 EXPECT_EQ(1U, site_instance
->active_frame_count());
2025 // Proxies should still exist.
2027 root_manager
->GetRenderFrameProxyHost(site_instance
.get()));
2029 iframe2
->GetRenderFrameProxyHost(site_instance
.get()));
2031 // Detach the second child FrameTreeNode. This should trigger cleanup of
2032 // RenderFrameProxyHosts in |site_instance|.
2033 iframe2
->current_frame_host()->OnMessageReceived(
2034 FrameHostMsg_Detach(iframe2
->current_frame_host()->GetRoutingID()));
2035 iframe2
= NULL
; // Was just destroyed.
2037 EXPECT_TRUE(delete_watcher1
.deleted());
2038 EXPECT_TRUE(delete_watcher2
.deleted());
2040 EXPECT_EQ(0U, site_instance
->active_frame_count());
2042 root_manager
->GetRenderFrameProxyHost(site_instance
.get()))
2043 << "Proxies should have been cleaned up";
2044 EXPECT_TRUE(site_instance
->HasOneRef())
2045 << "This SiteInstance should be destroyable now.";
2048 // Two tabs in the same process crash. The first tab is reloaded, and the second
2049 // tab navigates away without reloading. The second tab's navigation shouldn't
2050 // mess with the first tab's content. Motivated by http://crbug.com/473714.
2051 TEST_F(RenderFrameHostManagerTest
, TwoTabsCrashOneReloadsOneLeaves
) {
2052 base::CommandLine::ForCurrentProcess()->AppendSwitch(
2053 switches::kSitePerProcess
);
2055 const GURL
kUrl1("http://www.google.com/");
2056 const GURL
kUrl2("http://webkit.org/");
2057 const GURL
kUrl3("http://whatwg.org/");
2059 // |contents1| and |contents2| navigate to the same page and then crash.
2060 TestWebContents
* contents1
= contents();
2061 scoped_ptr
<TestWebContents
> contents2(
2062 TestWebContents::Create(browser_context(), contents1
->GetSiteInstance()));
2063 contents1
->NavigateAndCommit(kUrl1
);
2064 contents2
->NavigateAndCommit(kUrl1
);
2065 MockRenderProcessHost
* rph
= contents1
->GetMainFrame()->GetProcess();
2066 EXPECT_EQ(rph
, contents2
->GetMainFrame()->GetProcess());
2067 rph
->SimulateCrash();
2068 EXPECT_FALSE(contents1
->GetMainFrame()->IsRenderFrameLive());
2069 EXPECT_FALSE(contents2
->GetMainFrame()->IsRenderFrameLive());
2070 EXPECT_EQ(contents1
->GetSiteInstance(), contents2
->GetSiteInstance());
2072 // Reload |contents1|.
2073 contents1
->NavigateAndCommit(kUrl1
);
2074 EXPECT_TRUE(contents1
->GetMainFrame()->IsRenderFrameLive());
2075 EXPECT_FALSE(contents2
->GetMainFrame()->IsRenderFrameLive());
2076 EXPECT_EQ(contents1
->GetSiteInstance(), contents2
->GetSiteInstance());
2078 // |contents1| creates an out of process iframe.
2079 contents1
->GetMainFrame()->OnCreateChildFrame(
2080 contents1
->GetMainFrame()->GetProcess()->GetNextRoutingID(),
2081 std::string("frame_name"), SandboxFlags::NONE
);
2082 RenderFrameHostManager
* iframe
=
2083 contents()->GetFrameTree()->root()->child_at(0)->render_manager();
2084 NavigationEntryImpl
entry(NULL
/* instance */, -1 /* page_id */, kUrl2
,
2085 Referrer(kUrl1
, blink::WebReferrerPolicyDefault
),
2086 base::string16() /* title */,
2087 ui::PAGE_TRANSITION_LINK
,
2088 false /* is_renderer_init */);
2089 RenderFrameHostImpl
* cross_site
= NavigateToEntry(iframe
, entry
);
2090 iframe
->DidNavigateFrame(cross_site
, true);
2092 // A proxy to the iframe should now exist in the SiteInstance of the main
2094 EXPECT_NE(cross_site
->GetSiteInstance(), contents1
->GetSiteInstance());
2096 iframe
->GetRenderFrameProxyHost(contents1
->GetSiteInstance()));
2098 iframe
->GetRenderFrameProxyHost(contents2
->GetSiteInstance()));
2100 // Navigate |contents2| away from the sad tab (and thus away from the
2101 // SiteInstance of |contents1|). This should not destroy the proxies needed by
2102 // |contents1| -- that was http://crbug.com/473714.
2103 EXPECT_FALSE(contents2
->GetMainFrame()->IsRenderFrameLive());
2104 contents2
->NavigateAndCommit(kUrl3
);
2105 EXPECT_TRUE(contents2
->GetMainFrame()->IsRenderFrameLive());
2107 iframe
->GetRenderFrameProxyHost(contents1
->GetSiteInstance()));
2109 iframe
->GetRenderFrameProxyHost(contents2
->GetSiteInstance()));
2112 } // namespace content