1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/command_line.h"
6 #include "base/files/file_path.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "base/test/histogram_tester.h"
9 #include "base/time/time.h"
10 #include "content/browser/compositor/test/no_transport_image_transport_factory.h"
11 #include "content/browser/frame_host/cross_site_transferring_request.h"
12 #include "content/browser/frame_host/navigation_controller_impl.h"
13 #include "content/browser/frame_host/navigation_entry_impl.h"
14 #include "content/browser/frame_host/navigation_request.h"
15 #include "content/browser/frame_host/navigator.h"
16 #include "content/browser/frame_host/render_frame_host_manager.h"
17 #include "content/browser/frame_host/render_frame_proxy_host.h"
18 #include "content/browser/site_instance_impl.h"
19 #include "content/browser/webui/web_ui_controller_factory_registry.h"
20 #include "content/common/frame_messages.h"
21 #include "content/common/view_messages.h"
22 #include "content/public/browser/notification_details.h"
23 #include "content/public/browser/notification_service.h"
24 #include "content/public/browser/notification_source.h"
25 #include "content/public/browser/notification_types.h"
26 #include "content/public/browser/render_process_host.h"
27 #include "content/public/browser/render_widget_host_iterator.h"
28 #include "content/public/browser/web_contents_delegate.h"
29 #include "content/public/browser/web_contents_observer.h"
30 #include "content/public/browser/web_ui_controller.h"
31 #include "content/public/common/bindings_policy.h"
32 #include "content/public/common/content_switches.h"
33 #include "content/public/common/javascript_message_type.h"
34 #include "content/public/common/url_constants.h"
35 #include "content/public/common/url_utils.h"
36 #include "content/public/test/browser_test_utils.h"
37 #include "content/public/test/mock_render_process_host.h"
38 #include "content/public/test/test_notification_tracker.h"
39 #include "content/test/test_content_browser_client.h"
40 #include "content/test/test_content_client.h"
41 #include "content/test/test_render_frame_host.h"
42 #include "content/test/test_render_view_host.h"
43 #include "content/test/test_web_contents.h"
44 #include "net/base/load_flags.h"
45 #include "testing/gtest/include/gtest/gtest.h"
46 #include "third_party/WebKit/public/web/WebSandboxFlags.h"
47 #include "ui/base/page_transition_types.h"
52 class RenderFrameHostManagerTestWebUIControllerFactory
53 : public WebUIControllerFactory
{
55 RenderFrameHostManagerTestWebUIControllerFactory()
56 : should_create_webui_(false) {
58 ~RenderFrameHostManagerTestWebUIControllerFactory() override
{}
60 void set_should_create_webui(bool should_create_webui
) {
61 should_create_webui_
= should_create_webui
;
64 // WebUIFactory implementation.
65 WebUIController
* CreateWebUIControllerForURL(WebUI
* web_ui
,
66 const GURL
& url
) const override
{
67 if (!(should_create_webui_
&& HasWebUIScheme(url
)))
69 return new WebUIController(web_ui
);
72 WebUI::TypeID
GetWebUIType(BrowserContext
* browser_context
,
73 const GURL
& url
) const override
{
74 return WebUI::kNoWebUI
;
77 bool UseWebUIForURL(BrowserContext
* browser_context
,
78 const GURL
& url
) const override
{
79 return HasWebUIScheme(url
);
82 bool UseWebUIBindingsForURL(BrowserContext
* browser_context
,
83 const GURL
& url
) const override
{
84 return HasWebUIScheme(url
);
88 bool should_create_webui_
;
90 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostManagerTestWebUIControllerFactory
);
93 class BeforeUnloadFiredWebContentsDelegate
: public WebContentsDelegate
{
95 BeforeUnloadFiredWebContentsDelegate() {}
96 ~BeforeUnloadFiredWebContentsDelegate() override
{}
98 void BeforeUnloadFired(WebContents
* web_contents
,
100 bool* proceed_to_fire_unload
) override
{
101 *proceed_to_fire_unload
= proceed
;
105 DISALLOW_COPY_AND_ASSIGN(BeforeUnloadFiredWebContentsDelegate
);
108 class CloseWebContentsDelegate
: public WebContentsDelegate
{
110 CloseWebContentsDelegate() : close_called_(false) {}
111 ~CloseWebContentsDelegate() override
{}
113 void CloseContents(WebContents
* web_contents
) override
{
114 close_called_
= true;
117 bool is_closed() { return close_called_
; }
120 DISALLOW_COPY_AND_ASSIGN(CloseWebContentsDelegate
);
125 // This observer keeps track of the last deleted RenderViewHost to avoid
126 // accessing it and causing use-after-free condition.
127 class RenderViewHostDeletedObserver
: public WebContentsObserver
{
129 RenderViewHostDeletedObserver(RenderViewHost
* rvh
)
130 : WebContentsObserver(WebContents::FromRenderViewHost(rvh
)),
131 process_id_(rvh
->GetProcess()->GetID()),
132 routing_id_(rvh
->GetRoutingID()),
136 void RenderViewDeleted(RenderViewHost
* render_view_host
) override
{
137 if (render_view_host
->GetProcess()->GetID() == process_id_
&&
138 render_view_host
->GetRoutingID() == routing_id_
) {
152 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDeletedObserver
);
155 // This observer keeps track of the last created RenderFrameHost to allow tests
156 // to ensure that no RenderFrameHost objects are created when not expected.
157 class RenderFrameHostCreatedObserver
: public WebContentsObserver
{
159 RenderFrameHostCreatedObserver(WebContents
* web_contents
)
160 : WebContentsObserver(web_contents
),
164 void RenderFrameCreated(RenderFrameHost
* render_frame_host
) override
{
175 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostCreatedObserver
);
178 // This WebContents observer keep track of its RVH change.
179 class RenderViewHostChangedObserver
: public WebContentsObserver
{
181 RenderViewHostChangedObserver(WebContents
* web_contents
)
182 : WebContentsObserver(web_contents
), host_changed_(false) {}
184 // WebContentsObserver.
185 void RenderViewHostChanged(RenderViewHost
* old_host
,
186 RenderViewHost
* new_host
) override
{
187 host_changed_
= true;
190 bool DidHostChange() {
191 bool host_changed
= host_changed_
;
196 void Reset() { host_changed_
= false; }
200 DISALLOW_COPY_AND_ASSIGN(RenderViewHostChangedObserver
);
203 // This observer is used to check whether IPC messages are being filtered for
204 // swapped out RenderFrameHost objects. It observes the plugin crash and favicon
205 // update events, which the FilterMessagesWhileSwappedOut test simulates being
206 // sent. The test is successful if the event is not observed.
207 // See http://crbug.com/351815
208 class PluginFaviconMessageObserver
: public WebContentsObserver
{
210 PluginFaviconMessageObserver(WebContents
* web_contents
)
211 : WebContentsObserver(web_contents
),
212 plugin_crashed_(false),
213 favicon_received_(false) { }
215 void PluginCrashed(const base::FilePath
& plugin_path
,
216 base::ProcessId plugin_pid
) override
{
217 plugin_crashed_
= true;
220 void DidUpdateFaviconURL(const std::vector
<FaviconURL
>& candidates
) override
{
221 favicon_received_
= true;
224 bool plugin_crashed() {
225 return plugin_crashed_
;
228 bool favicon_received() {
229 return favicon_received_
;
233 bool plugin_crashed_
;
234 bool favicon_received_
;
236 DISALLOW_COPY_AND_ASSIGN(PluginFaviconMessageObserver
);
241 class RenderFrameHostManagerTest
: public RenderViewHostImplTestHarness
{
243 void SetUp() override
{
244 RenderViewHostImplTestHarness::SetUp();
245 WebUIControllerFactory::RegisterFactory(&factory_
);
246 #if !defined(OS_ANDROID)
247 ImageTransportFactory::InitializeForUnitTests(
248 make_scoped_ptr(new NoTransportImageTransportFactory
));
252 void TearDown() override
{
253 #if !defined(OS_ANDROID)
254 ImageTransportFactory::Terminate();
256 RenderViewHostImplTestHarness::TearDown();
257 WebUIControllerFactory::UnregisterFactoryForTesting(&factory_
);
260 void set_should_create_webui(bool should_create_webui
) {
261 factory_
.set_should_create_webui(should_create_webui
);
264 void NavigateActiveAndCommit(const GURL
& url
) {
265 // Note: we navigate the active RenderFrameHost because previous navigations
266 // won't have committed yet, so NavigateAndCommit does the wrong thing
268 controller().LoadURL(
269 url
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
270 int entry_id
= controller().GetPendingEntry()->GetUniqueID();
272 // Simulate the BeforeUnload_ACK that is received from the current renderer
273 // for a cross-site navigation.
274 // PlzNavigate: it is necessary to call PrepareForCommit before getting the
275 // main and the pending frame because when we are trying to navigate to a
276 // WebUI from a new tab, a RenderFrameHost is created to display it that is
277 // committed immediately (since it is a new tab). Therefore the main frame
278 // is replaced without a pending frame being created, and we don't get the
279 // right values for the RFH to navigate: we try to use the old one that has
280 // been deleted in the meantime.
281 contents()->GetMainFrame()->PrepareForCommit();
283 TestRenderFrameHost
* old_rfh
= contents()->GetMainFrame();
284 TestRenderFrameHost
* active_rfh
= contents()->GetPendingMainFrame()
285 ? contents()->GetPendingMainFrame()
287 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, old_rfh
->rfh_state());
289 // Commit the navigation with a new page ID.
290 int32 max_page_id
= contents()->GetMaxPageIDForSiteInstance(
291 active_rfh
->GetSiteInstance());
293 // Use an observer to avoid accessing a deleted renderer later on when the
294 // state is being checked.
295 RenderFrameDeletedObserver
rfh_observer(old_rfh
);
296 RenderViewHostDeletedObserver
rvh_observer(old_rfh
->GetRenderViewHost());
297 active_rfh
->SendNavigate(max_page_id
+ 1, entry_id
, true, url
);
299 // Make sure that we start to run the unload handler at the time of commit.
300 bool expecting_rfh_shutdown
= false;
301 if (old_rfh
!= active_rfh
&& !rfh_observer
.deleted()) {
302 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
,
303 old_rfh
->rfh_state());
304 if (!old_rfh
->GetSiteInstance()->active_frame_count() ||
305 RenderFrameHostManager::IsSwappedOutStateForbidden()) {
306 expecting_rfh_shutdown
= true;
308 old_rfh
->frame_tree_node()->render_manager()->IsPendingDeletion(
313 // Simulate the swap out ACK coming from the pending renderer. This should
314 // either shut down the old RFH or leave it in a swapped out state.
315 if (old_rfh
!= active_rfh
) {
316 old_rfh
->OnSwappedOut();
317 if (expecting_rfh_shutdown
) {
318 EXPECT_TRUE(rfh_observer
.deleted());
319 if (!RenderFrameHostManager::IsSwappedOutStateForbidden()) {
320 EXPECT_TRUE(rvh_observer
.deleted());
323 EXPECT_EQ(RenderFrameHostImpl::STATE_SWAPPED_OUT
,
324 old_rfh
->rfh_state());
327 EXPECT_EQ(active_rfh
, contents()->GetMainFrame());
328 EXPECT_EQ(NULL
, contents()->GetPendingMainFrame());
331 bool ShouldSwapProcesses(RenderFrameHostManager
* manager
,
332 const NavigationEntryImpl
* current_entry
,
333 const NavigationEntryImpl
* new_entry
) const {
335 BrowserContext
* browser_context
=
336 manager
->delegate_
->GetControllerForRenderManager().GetBrowserContext();
337 const GURL
& current_effective_url
= current_entry
?
338 SiteInstanceImpl::GetEffectiveURL(browser_context
,
339 current_entry
->GetURL()) :
340 manager
->render_frame_host_
->GetSiteInstance()->GetSiteURL();
341 bool current_is_view_source_mode
= current_entry
?
342 current_entry
->IsViewSourceMode() : new_entry
->IsViewSourceMode();
343 return manager
->ShouldSwapBrowsingInstancesForNavigation(
344 current_effective_url
,
345 current_is_view_source_mode
,
346 new_entry
->site_instance(),
347 SiteInstanceImpl::GetEffectiveURL(browser_context
, new_entry
->GetURL()),
348 new_entry
->IsViewSourceMode());
351 // Creates a test RenderFrameHost that's swapped out.
352 TestRenderFrameHost
* CreateSwappedOutRenderFrameHost() {
353 const GURL
kChromeURL("chrome://foo");
354 const GURL
kDestUrl("http://www.google.com/");
356 // Navigate our first tab to a chrome url and then to the destination.
357 NavigateActiveAndCommit(kChromeURL
);
358 TestRenderFrameHost
* ntp_rfh
= contents()->GetMainFrame();
360 // Navigate to a cross-site URL.
361 contents()->GetController().LoadURL(
362 kDestUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
363 int entry_id
= contents()->GetController().GetPendingEntry()->GetUniqueID();
364 contents()->GetMainFrame()->PrepareForCommit();
365 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
367 // Manually increase the number of active frames in the
368 // SiteInstance that ntp_rfh belongs to, to prevent it from being
369 // destroyed when it gets swapped out.
370 ntp_rfh
->GetSiteInstance()->increment_active_frame_count();
372 TestRenderFrameHost
* dest_rfh
= contents()->GetPendingMainFrame();
374 EXPECT_NE(ntp_rfh
, dest_rfh
);
376 // BeforeUnload finishes.
377 ntp_rfh
->SendBeforeUnloadACK(true);
379 dest_rfh
->SendNavigate(101, entry_id
, true, kDestUrl
);
380 ntp_rfh
->OnSwappedOut();
382 EXPECT_TRUE(ntp_rfh
->is_swapped_out());
386 // Returns the RenderFrameHost that should be used in the navigation to
388 RenderFrameHostImpl
* NavigateToEntry(
389 RenderFrameHostManager
* manager
,
390 const NavigationEntryImpl
& entry
) {
391 // Tests currently only navigate using main frame FrameNavigationEntries.
392 FrameNavigationEntry
* frame_entry
= entry
.root_node()->frame_entry
.get();
393 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
394 switches::kEnableBrowserSideNavigation
)) {
395 scoped_ptr
<NavigationRequest
> navigation_request
=
396 NavigationRequest::CreateBrowserInitiated(
397 manager
->frame_tree_node_
, frame_entry
->url(),
398 frame_entry
->referrer(), *frame_entry
, entry
,
399 FrameMsg_Navigate_Type::NORMAL
, false, base::TimeTicks::Now(),
400 static_cast<NavigationControllerImpl
*>(&controller()));
401 TestRenderFrameHost
* frame_host
= static_cast<TestRenderFrameHost
*>(
402 manager
->GetFrameHostForNavigation(*navigation_request
));
404 frame_host
->set_pending_commit(true);
408 return manager
->Navigate(frame_entry
->url(), *frame_entry
, entry
);
411 // Returns the pending RenderFrameHost.
412 // PlzNavigate: returns the speculative RenderFrameHost.
413 RenderFrameHostImpl
* GetPendingFrameHost(
414 RenderFrameHostManager
* manager
) {
415 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
416 switches::kEnableBrowserSideNavigation
)) {
417 return manager
->speculative_render_frame_host_
.get();
419 return manager
->pending_frame_host();
423 RenderFrameHostManagerTestWebUIControllerFactory factory_
;
426 // Tests that when you navigate from a chrome:// url to another page, and
427 // then do that same thing in another tab, that the two resulting pages have
428 // different SiteInstances, BrowsingInstances, and RenderProcessHosts. This is
429 // a regression test for bug 9364.
430 TEST_F(RenderFrameHostManagerTest
, NewTabPageProcesses
) {
431 set_should_create_webui(true);
432 const GURL
kChromeUrl("chrome://foo");
433 const GURL
kDestUrl("http://www.google.com/");
435 // Navigate our first tab to the chrome url and then to the destination,
436 // ensuring we grant bindings to the chrome URL.
437 NavigateActiveAndCommit(kChromeUrl
);
438 EXPECT_TRUE(active_rvh()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
439 NavigateActiveAndCommit(kDestUrl
);
441 EXPECT_FALSE(contents()->GetPendingMainFrame());
443 // Make a second tab.
444 scoped_ptr
<TestWebContents
> contents2(
445 TestWebContents::Create(browser_context(), NULL
));
447 // Load the two URLs in the second tab. Note that the first navigation creates
448 // a RFH that's not pending (since there is no cross-site transition), so
449 // we use the committed one.
450 contents2
->GetController().LoadURL(
451 kChromeUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
452 int entry_id
= contents2
->GetController().GetPendingEntry()->GetUniqueID();
453 contents2
->GetMainFrame()->PrepareForCommit();
454 TestRenderFrameHost
* ntp_rfh2
= contents2
->GetMainFrame();
455 EXPECT_FALSE(contents2
->CrossProcessNavigationPending());
456 ntp_rfh2
->SendNavigate(100, entry_id
, true, kChromeUrl
);
458 // The second one is the opposite, creating a cross-site transition and
459 // requiring a beforeunload ack.
460 contents2
->GetController().LoadURL(
461 kDestUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
462 entry_id
= contents2
->GetController().GetPendingEntry()->GetUniqueID();
463 contents2
->GetMainFrame()->PrepareForCommit();
464 EXPECT_TRUE(contents2
->CrossProcessNavigationPending());
465 TestRenderFrameHost
* dest_rfh2
= contents2
->GetPendingMainFrame();
466 ASSERT_TRUE(dest_rfh2
);
468 dest_rfh2
->SendNavigate(101, entry_id
, true, kDestUrl
);
470 // The two RFH's should be different in every way.
471 EXPECT_NE(contents()->GetMainFrame()->GetProcess(), dest_rfh2
->GetProcess());
472 EXPECT_NE(contents()->GetMainFrame()->GetSiteInstance(),
473 dest_rfh2
->GetSiteInstance());
474 EXPECT_FALSE(dest_rfh2
->GetSiteInstance()->IsRelatedSiteInstance(
475 contents()->GetMainFrame()->GetSiteInstance()));
477 // Navigate both to the new tab page, and verify that they share a
478 // RenderProcessHost (not a SiteInstance).
479 NavigateActiveAndCommit(kChromeUrl
);
480 EXPECT_FALSE(contents()->GetPendingMainFrame());
482 contents2
->GetController().LoadURL(
483 kChromeUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
484 entry_id
= contents2
->GetController().GetPendingEntry()->GetUniqueID();
485 contents2
->GetMainFrame()->PrepareForCommit();
486 contents2
->GetPendingMainFrame()->SendNavigate(102, entry_id
, true,
489 EXPECT_NE(contents()->GetMainFrame()->GetSiteInstance(),
490 contents2
->GetMainFrame()->GetSiteInstance());
491 EXPECT_EQ(contents()->GetMainFrame()->GetSiteInstance()->GetProcess(),
492 contents2
->GetMainFrame()->GetSiteInstance()->GetProcess());
495 // Ensure that the browser ignores most IPC messages that arrive from a
496 // RenderViewHost that has been swapped out. We do not want to take
497 // action on requests from a non-active renderer. The main exception is
498 // for synchronous messages, which cannot be ignored without leaving the
499 // renderer in a stuck state. See http://crbug.com/93427.
500 TEST_F(RenderFrameHostManagerTest
, FilterMessagesWhileSwappedOut
) {
501 const GURL
kChromeURL("chrome://foo");
502 const GURL
kDestUrl("http://www.google.com/");
503 std::vector
<FaviconURL
> icons
;
505 // Navigate our first tab to a chrome url and then to the destination.
506 NavigateActiveAndCommit(kChromeURL
);
507 TestRenderFrameHost
* ntp_rfh
= contents()->GetMainFrame();
508 TestRenderViewHost
* ntp_rvh
= ntp_rfh
->GetRenderViewHost();
510 // Send an update favicon message and make sure it works.
512 PluginFaviconMessageObserver
observer(contents());
513 EXPECT_TRUE(ntp_rfh
->GetRenderViewHost()->OnMessageReceived(
514 ViewHostMsg_UpdateFaviconURL(
515 ntp_rfh
->GetRenderViewHost()->GetRoutingID(), icons
)));
516 EXPECT_TRUE(observer
.favicon_received());
518 // Create one more frame in the same SiteInstance where ntp_rfh
519 // exists so that it doesn't get deleted on navigation to another
521 ntp_rfh
->GetSiteInstance()->increment_active_frame_count();
523 // Navigate to a cross-site URL.
524 NavigateActiveAndCommit(kDestUrl
);
525 TestRenderFrameHost
* dest_rfh
= contents()->GetMainFrame();
526 ASSERT_TRUE(dest_rfh
);
527 EXPECT_NE(ntp_rfh
, dest_rfh
);
529 // The new RVH should be able to update its favicon.
531 PluginFaviconMessageObserver
observer(contents());
533 dest_rfh
->GetRenderViewHost()->OnMessageReceived(
534 ViewHostMsg_UpdateFaviconURL(
535 dest_rfh
->GetRenderViewHost()->GetRoutingID(), icons
)));
536 EXPECT_TRUE(observer
.favicon_received());
539 // The old renderer, being slow, now updates the favicon. It should be
540 // filtered out and not take effect.
542 PluginFaviconMessageObserver
observer(contents());
544 ntp_rvh
->OnMessageReceived(
545 ViewHostMsg_UpdateFaviconURL(
546 dest_rfh
->GetRenderViewHost()->GetRoutingID(), icons
)));
547 EXPECT_FALSE(observer
.favicon_received());
550 // In --site-per-process, the RenderFrameHost is deleted on cross-process
551 // navigation, so the rest of the test case doesn't apply.
552 if (RenderFrameHostManager::IsSwappedOutStateForbidden()) {
556 #if defined(ENABLE_PLUGINS)
557 // The same logic should apply to RenderFrameHosts as well and routing through
558 // swapped out RFH shouldn't be allowed. Use a PluginCrashObserver to check
559 // if the IPC message is allowed through or not.
561 PluginFaviconMessageObserver
observer(contents());
562 EXPECT_TRUE(ntp_rfh
->OnMessageReceived(
563 FrameHostMsg_PluginCrashed(
564 ntp_rfh
->GetRoutingID(), base::FilePath(), 0)));
565 EXPECT_FALSE(observer
.plugin_crashed());
569 // We cannot filter out synchronous IPC messages, because the renderer would
570 // be left waiting for a reply. We pick RunBeforeUnloadConfirm as an example
571 // that can run easily within a unit test, and that needs to receive a reply
572 // without showing an actual dialog.
573 MockRenderProcessHost
* ntp_process_host
= ntp_rfh
->GetProcess();
574 ntp_process_host
->sink().ClearMessages();
575 const base::string16 msg
= base::ASCIIToUTF16("Message");
577 base::string16 unused
;
578 FrameHostMsg_RunBeforeUnloadConfirm
before_unload_msg(
579 ntp_rfh
->GetRoutingID(), kChromeURL
, msg
, false, &result
, &unused
);
580 // Enable pumping for check in BrowserMessageFilter::CheckCanDispatchOnUI.
581 before_unload_msg
.EnableMessagePumping();
582 EXPECT_TRUE(ntp_rfh
->OnMessageReceived(before_unload_msg
));
583 EXPECT_TRUE(ntp_process_host
->sink().GetUniqueMessageMatching(IPC_REPLY_ID
));
585 // Also test RunJavaScriptMessage.
586 ntp_process_host
->sink().ClearMessages();
587 FrameHostMsg_RunJavaScriptMessage
js_msg(
588 ntp_rfh
->GetRoutingID(), msg
, msg
, kChromeURL
,
589 JAVASCRIPT_MESSAGE_TYPE_CONFIRM
, &result
, &unused
);
590 js_msg
.EnableMessagePumping();
591 EXPECT_TRUE(ntp_rfh
->OnMessageReceived(js_msg
));
592 EXPECT_TRUE(ntp_process_host
->sink().GetUniqueMessageMatching(IPC_REPLY_ID
));
595 // Test that the ViewHostMsg_UpdateFaviconURL IPC message is ignored if the
596 // renderer is in the STATE_PENDING_SWAP_OUT_STATE. The favicon code assumes
597 // that it only gets ViewHostMsg_UpdateFaviconURL messages for the most recently
598 // committed navigation for each WebContentsImpl.
599 TEST_F(RenderFrameHostManagerTest
, UpdateFaviconURLWhilePendingSwapOut
) {
600 const GURL
kChromeURL("chrome://foo");
601 const GURL
kDestUrl("http://www.google.com/");
602 std::vector
<FaviconURL
> icons
;
604 // Navigate our first tab to a chrome url and then to the destination.
605 NavigateActiveAndCommit(kChromeURL
);
606 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
608 // Send an update favicon message and make sure it works.
610 PluginFaviconMessageObserver
observer(contents());
611 EXPECT_TRUE(rfh1
->GetRenderViewHost()->OnMessageReceived(
612 ViewHostMsg_UpdateFaviconURL(
613 rfh1
->GetRenderViewHost()->GetRoutingID(), icons
)));
614 EXPECT_TRUE(observer
.favicon_received());
617 // Create one more frame in the same SiteInstance where |rfh1| exists so that
618 // it doesn't get deleted on navigation to another site.
619 rfh1
->GetSiteInstance()->increment_active_frame_count();
621 // Navigate to a cross-site URL and commit the new page.
622 controller().LoadURL(
623 kDestUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
624 int entry_id
= controller().GetPendingEntry()->GetUniqueID();
625 contents()->GetMainFrame()->PrepareForCommit();
626 TestRenderFrameHost
* rfh2
= contents()->GetPendingMainFrame();
627 contents()->TestDidNavigate(rfh2
, 1, entry_id
, true, kDestUrl
,
628 ui::PAGE_TRANSITION_TYPED
);
629 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
630 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
632 // The new RVH should be able to update its favicons.
634 PluginFaviconMessageObserver
observer(contents());
635 EXPECT_TRUE(rfh2
->GetRenderViewHost()->OnMessageReceived(
636 ViewHostMsg_UpdateFaviconURL(rfh2
->GetRenderViewHost()->GetRoutingID(),
638 EXPECT_TRUE(observer
.favicon_received());
641 // The old renderer, being slow, now updates its favicons. The message should
644 PluginFaviconMessageObserver
observer(contents());
645 EXPECT_TRUE(rfh1
->GetRenderViewHost()->OnMessageReceived(
646 ViewHostMsg_UpdateFaviconURL(rfh1
->GetRenderViewHost()->GetRoutingID(),
648 EXPECT_FALSE(observer
.favicon_received());
652 // Ensure that frames aren't added to the frame tree, if the message is coming
653 // from a process different than the parent frame's current RenderFrameHost
654 // process. Otherwise it is possible to have collisions of routing ids, as they
655 // are scoped per process. See https://crbug.com/415059.
656 TEST_F(RenderFrameHostManagerTest
, DropCreateChildFrameWhileSwappedOut
) {
657 const GURL
kUrl1("http://foo.com");
658 const GURL
kUrl2("http://www.google.com/");
660 // This test is invalid in --site-per-process mode, as swapped-out is no
662 if (RenderFrameHostManager::IsSwappedOutStateForbidden()) {
666 // Navigate to the first site.
667 NavigateActiveAndCommit(kUrl1
);
668 TestRenderFrameHost
* initial_rfh
= contents()->GetMainFrame();
670 RenderFrameHostCreatedObserver
observer(contents());
671 initial_rfh
->OnCreateChildFrame(
672 initial_rfh
->GetProcess()->GetNextRoutingID(),
673 blink::WebTreeScopeType::Document
, std::string(),
674 blink::WebSandboxFlags::None
);
675 EXPECT_TRUE(observer
.created());
678 // Create one more frame in the same SiteInstance where initial_rfh
679 // exists so that initial_rfh doesn't get deleted on navigation to another
681 initial_rfh
->GetSiteInstance()->increment_active_frame_count();
683 // Navigate to a cross-site URL.
684 NavigateActiveAndCommit(kUrl2
);
685 EXPECT_TRUE(initial_rfh
->is_swapped_out());
687 TestRenderFrameHost
* dest_rfh
= contents()->GetMainFrame();
688 ASSERT_TRUE(dest_rfh
);
689 EXPECT_NE(initial_rfh
, dest_rfh
);
692 // Since the old RFH is now swapped out, it shouldn't process any messages
693 // to create child frames.
694 RenderFrameHostCreatedObserver
observer(contents());
695 initial_rfh
->OnCreateChildFrame(
696 initial_rfh
->GetProcess()->GetNextRoutingID(),
697 blink::WebTreeScopeType::Document
, std::string(),
698 blink::WebSandboxFlags::None
);
699 EXPECT_FALSE(observer
.created());
703 TEST_F(RenderFrameHostManagerTest
, WhiteListSwapCompositorFrame
) {
704 // TODO(nasko): Check with kenrb whether this test can be rewritten and
705 // whether it makes sense when swapped out is replaced with proxies.
706 if (RenderFrameHostManager::IsSwappedOutStateForbidden()) {
709 TestRenderFrameHost
* swapped_out_rfh
= CreateSwappedOutRenderFrameHost();
710 TestRenderWidgetHostView
* swapped_out_rwhv
=
711 static_cast<TestRenderWidgetHostView
*>(
712 swapped_out_rfh
->GetRenderViewHost()->GetView());
713 EXPECT_FALSE(swapped_out_rwhv
->did_swap_compositor_frame());
715 MockRenderProcessHost
* process_host
= swapped_out_rfh
->GetProcess();
716 process_host
->sink().ClearMessages();
718 cc::CompositorFrame frame
;
719 ViewHostMsg_SwapCompositorFrame
msg(
720 rvh()->GetRoutingID(), 0, frame
, std::vector
<IPC::Message
>());
722 EXPECT_TRUE(swapped_out_rfh
->render_view_host()->OnMessageReceived(msg
));
723 EXPECT_TRUE(swapped_out_rwhv
->did_swap_compositor_frame());
726 // Test if RenderViewHost::GetRenderWidgetHosts() only returns active
728 TEST_F(RenderFrameHostManagerTest
, GetRenderWidgetHostsReturnsActiveViews
) {
729 // This test is invalid in --site-per-process mode, as swapped-out is no
731 if (RenderFrameHostManager::IsSwappedOutStateForbidden()) {
735 TestRenderFrameHost
* swapped_out_rfh
= CreateSwappedOutRenderFrameHost();
736 EXPECT_TRUE(swapped_out_rfh
->is_swapped_out());
738 scoped_ptr
<RenderWidgetHostIterator
> widgets(
739 RenderWidgetHost::GetRenderWidgetHosts());
740 // We know that there is the only one active widget. Another view is
741 // now swapped out, so the swapped out view is not included in the
743 RenderWidgetHost
* widget
= widgets
->GetNextHost();
744 EXPECT_FALSE(widgets
->GetNextHost());
745 RenderViewHost
* rvh
= RenderViewHost::From(widget
);
746 EXPECT_TRUE(static_cast<RenderViewHostImpl
*>(rvh
)->is_active());
749 // Test if RenderViewHost::GetRenderWidgetHosts() returns a subset of
750 // RenderViewHostImpl::GetAllRenderWidgetHosts().
751 // RenderViewHost::GetRenderWidgetHosts() returns only active widgets, but
752 // RenderViewHostImpl::GetAllRenderWidgetHosts() returns everything
753 // including swapped out ones.
754 TEST_F(RenderFrameHostManagerTest
,
755 GetRenderWidgetHostsWithinGetAllRenderWidgetHosts
) {
756 // This test is invalid in --site-per-process mode, as swapped-out is no
758 if (RenderFrameHostManager::IsSwappedOutStateForbidden()) {
762 TestRenderFrameHost
* swapped_out_rfh
= CreateSwappedOutRenderFrameHost();
763 EXPECT_TRUE(swapped_out_rfh
->is_swapped_out());
765 scoped_ptr
<RenderWidgetHostIterator
> widgets(
766 RenderWidgetHost::GetRenderWidgetHosts());
768 while (RenderWidgetHost
* w
= widgets
->GetNextHost()) {
770 scoped_ptr
<RenderWidgetHostIterator
> all_widgets(
771 RenderWidgetHostImpl::GetAllRenderWidgetHosts());
772 while (RenderWidgetHost
* widget
= all_widgets
->GetNextHost()) {
782 // Test if SiteInstanceImpl::active_frame_count() is correctly updated
783 // as frames in a SiteInstance get swapped out and in.
784 TEST_F(RenderFrameHostManagerTest
, ActiveFrameCountWhileSwappingInAndOut
) {
785 const GURL
kUrl1("http://www.google.com/");
786 const GURL
kUrl2("http://www.chromium.org/");
788 // Navigate to an initial URL.
789 contents()->NavigateAndCommit(kUrl1
);
790 TestRenderFrameHost
* rfh1
= main_test_rfh();
792 SiteInstanceImpl
* instance1
= rfh1
->GetSiteInstance();
793 EXPECT_EQ(instance1
->active_frame_count(), 1U);
795 // Create 2 new tabs and simulate them being the opener chain for the main
796 // tab. They should be in the same SiteInstance.
797 scoped_ptr
<TestWebContents
> opener1(
798 TestWebContents::Create(browser_context(), instance1
));
799 contents()->SetOpener(opener1
.get());
801 scoped_ptr
<TestWebContents
> opener2(
802 TestWebContents::Create(browser_context(), instance1
));
803 opener1
->SetOpener(opener2
.get());
805 EXPECT_EQ(instance1
->active_frame_count(), 3U);
807 // Navigate to a cross-site URL (different SiteInstance but same
808 // BrowsingInstance).
809 contents()->NavigateAndCommit(kUrl2
);
810 TestRenderFrameHost
* rfh2
= main_test_rfh();
811 SiteInstanceImpl
* instance2
= rfh2
->GetSiteInstance();
813 // rvh2 is on chromium.org which is different from google.com on
814 // which other tabs are.
815 EXPECT_EQ(instance2
->active_frame_count(), 1U);
817 // There are two active views on google.com now.
818 EXPECT_EQ(instance1
->active_frame_count(), 2U);
820 // Navigate to the original origin (google.com).
821 contents()->NavigateAndCommit(kUrl1
);
823 EXPECT_EQ(instance1
->active_frame_count(), 3U);
826 // This deletes a WebContents when the given RVH is deleted. This is
827 // only for testing whether deleting an RVH does not cause any UaF in
828 // other parts of the system. For now, this class is only used for the
829 // next test cases to detect the bug mentioned at
830 // http://crbug.com/259859.
831 class RenderViewHostDestroyer
: public WebContentsObserver
{
833 RenderViewHostDestroyer(RenderViewHost
* render_view_host
,
834 WebContents
* web_contents
)
835 : WebContentsObserver(WebContents::FromRenderViewHost(render_view_host
)),
836 render_view_host_(render_view_host
),
837 web_contents_(web_contents
) {}
839 void RenderViewDeleted(RenderViewHost
* render_view_host
) override
{
840 if (render_view_host
== render_view_host_
)
841 delete web_contents_
;
845 RenderViewHost
* render_view_host_
;
846 WebContents
* web_contents_
;
848 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestroyer
);
851 // Test if ShutdownRenderViewHostsInSiteInstance() does not touch any
852 // RenderWidget that has been freed while deleting a RenderViewHost in
853 // a previous iteration. This is a regression test for
854 // http://crbug.com/259859.
855 TEST_F(RenderFrameHostManagerTest
,
856 DetectUseAfterFreeInShutdownRenderViewHostsInSiteInstance
) {
857 const GURL
kChromeURL("chrome://newtab");
858 const GURL
kUrl1("http://www.google.com");
859 const GURL
kUrl2("http://www.chromium.org");
861 // Navigate our first tab to a chrome url and then to the destination.
862 NavigateActiveAndCommit(kChromeURL
);
863 TestRenderFrameHost
* ntp_rfh
= contents()->GetMainFrame();
865 // Create one more tab and navigate to kUrl1. web_contents is not
866 // wrapped as scoped_ptr since it intentionally deleted by destroyer
867 // below as part of this test.
868 TestWebContents
* web_contents
=
869 TestWebContents::Create(browser_context(), ntp_rfh
->GetSiteInstance());
870 web_contents
->NavigateAndCommit(kUrl1
);
871 RenderViewHostDestroyer
destroyer(ntp_rfh
->GetRenderViewHost(),
874 // This causes the first tab to navigate to kUrl2, which destroys
875 // the ntp_rfh in ShutdownRenderViewHostsInSiteInstance(). When
876 // ntp_rfh is destroyed, it also destroys the RVHs in web_contents
877 // too. This can test whether
878 // SiteInstanceImpl::ShutdownRenderViewHostsInSiteInstance() can
879 // touch any object freed in this way or not while iterating through
881 contents()->NavigateAndCommit(kUrl2
);
884 // When there is an error with the specified page, renderer exits view-source
885 // mode. See WebFrameImpl::DidFail(). We check by this test that
886 // EnableViewSourceMode message is sent on every navigation regardless
887 // RenderView is being newly created or reused.
888 TEST_F(RenderFrameHostManagerTest
, AlwaysSendEnableViewSourceMode
) {
889 const GURL
kChromeUrl("chrome://foo/");
890 const GURL
kUrl("http://foo/");
891 const GURL
kViewSourceUrl("view-source:http://foo/");
893 // We have to navigate to some page at first since without this, the first
894 // navigation will reuse the SiteInstance created by Init(), and the second
895 // one will create a new SiteInstance. Because current_instance and
896 // new_instance will be different, a new RenderViewHost will be created for
897 // the second navigation. We have to avoid this in order to exercise the
899 NavigateActiveAndCommit(kChromeUrl
);
901 // Navigate. Note that "view source" URLs are implemented by putting the RFH
902 // into a view-source mode and then navigating to the inner URL, so that's why
903 // the bare URL is what's committed and returned by the last committed entry's
905 controller().LoadURL(
906 kViewSourceUrl
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
907 int entry_id
= controller().GetPendingEntry()->GetUniqueID();
909 // Simulate response from RenderFrame for DispatchBeforeUnload.
910 contents()->GetMainFrame()->PrepareForCommit();
911 ASSERT_TRUE(contents()->GetPendingMainFrame())
912 << "Expected new pending RenderFrameHost to be created.";
913 RenderFrameHost
* last_rfh
= contents()->GetPendingMainFrame();
915 contents()->GetMaxPageIDForSiteInstance(last_rfh
->GetSiteInstance()) + 1;
916 contents()->GetPendingMainFrame()->SendNavigate(new_id
, entry_id
, true, kUrl
);
918 EXPECT_EQ(1, controller().GetLastCommittedEntryIndex());
919 NavigationEntry
* last_committed
= controller().GetLastCommittedEntry();
920 ASSERT_NE(nullptr, last_committed
);
921 EXPECT_EQ(kUrl
, last_committed
->GetURL());
922 EXPECT_EQ(kViewSourceUrl
, last_committed
->GetVirtualURL());
923 EXPECT_FALSE(controller().GetPendingEntry());
924 // Because we're using TestWebContents and TestRenderViewHost in this
925 // unittest, no one calls WebContentsImpl::RenderViewCreated(). So, we see no
926 // EnableViewSourceMode message, here.
928 // Clear queued messages before load.
929 process()->sink().ClearMessages();
932 controller().LoadURL(
933 kViewSourceUrl
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
934 entry_id
= controller().GetPendingEntry()->GetUniqueID();
935 contents()->GetMainFrame()->PrepareForCommit();
937 // The same RenderViewHost should be reused.
938 EXPECT_FALSE(contents()->GetPendingMainFrame());
939 EXPECT_EQ(last_rfh
, contents()->GetMainFrame());
941 // The renderer sends a commit.
942 contents()->GetMainFrame()->SendNavigateWithTransition(
943 new_id
, entry_id
, false, kUrl
, ui::PAGE_TRANSITION_TYPED
);
944 EXPECT_EQ(1, controller().GetLastCommittedEntryIndex());
945 EXPECT_FALSE(controller().GetPendingEntry());
947 // New message should be sent out to make sure to enter view-source mode.
948 EXPECT_TRUE(process()->sink().GetUniqueMessageMatching(
949 ViewMsg_EnableViewSourceMode::ID
));
952 // Tests the Init function by checking the initial RenderViewHost.
953 TEST_F(RenderFrameHostManagerTest
, Init
) {
954 // Using TestBrowserContext.
955 SiteInstanceImpl
* instance
=
956 static_cast<SiteInstanceImpl
*>(SiteInstance::Create(browser_context()));
957 EXPECT_FALSE(instance
->HasSite());
959 scoped_ptr
<TestWebContents
> web_contents(
960 TestWebContents::Create(browser_context(), instance
));
962 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
963 RenderViewHostImpl
* rvh
= manager
->current_host();
964 RenderFrameHostImpl
* rfh
= manager
->current_frame_host();
967 EXPECT_EQ(rvh
, rfh
->render_view_host());
968 EXPECT_EQ(instance
, rvh
->GetSiteInstance());
969 EXPECT_EQ(web_contents
.get(), rvh
->GetDelegate());
970 EXPECT_EQ(web_contents
.get(), rfh
->delegate());
971 EXPECT_TRUE(manager
->GetRenderWidgetHostView());
972 EXPECT_FALSE(manager
->pending_render_view_host());
975 // Tests the Navigate function. We navigate three sites consecutively and check
976 // how the pending/committed RenderViewHost are modified.
977 TEST_F(RenderFrameHostManagerTest
, Navigate
) {
978 SiteInstance
* instance
= SiteInstance::Create(browser_context());
980 scoped_ptr
<TestWebContents
> web_contents(
981 TestWebContents::Create(browser_context(), instance
));
982 RenderViewHostChangedObserver
change_observer(web_contents
.get());
984 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
985 RenderFrameHostImpl
* host
= NULL
;
987 // 1) The first navigation. --------------------------
988 const GURL
kUrl1("http://www.google.com/");
989 NavigationEntryImpl
entry1(
990 NULL
/* instance */, -1 /* page_id */, kUrl1
, Referrer(),
991 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED
,
992 false /* is_renderer_init */);
993 host
= NavigateToEntry(manager
, entry1
);
995 // The RenderFrameHost created in Init will be reused.
996 EXPECT_TRUE(host
== manager
->current_frame_host());
997 EXPECT_FALSE(GetPendingFrameHost(manager
));
1000 manager
->DidNavigateFrame(host
, true);
1001 // Commit to SiteInstance should be delayed until RenderFrame commit.
1002 EXPECT_TRUE(host
== manager
->current_frame_host());
1004 EXPECT_FALSE(host
->GetSiteInstance()->HasSite());
1005 host
->GetSiteInstance()->SetSite(kUrl1
);
1007 // 2) Navigate to next site. -------------------------
1008 const GURL
kUrl2("http://www.google.com/foo");
1009 NavigationEntryImpl
entry2(
1010 NULL
/* instance */, -1 /* page_id */, kUrl2
,
1011 Referrer(kUrl1
, blink::WebReferrerPolicyDefault
),
1012 base::string16() /* title */, ui::PAGE_TRANSITION_LINK
,
1013 true /* is_renderer_init */);
1014 host
= NavigateToEntry(manager
, entry2
);
1016 // The RenderFrameHost created in Init will be reused.
1017 EXPECT_TRUE(host
== manager
->current_frame_host());
1018 EXPECT_FALSE(GetPendingFrameHost(manager
));
1021 manager
->DidNavigateFrame(host
, true);
1022 EXPECT_TRUE(host
== manager
->current_frame_host());
1024 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
1026 // 3) Cross-site navigate to next site. --------------
1027 const GURL
kUrl3("http://webkit.org/");
1028 NavigationEntryImpl
entry3(
1029 NULL
/* instance */, -1 /* page_id */, kUrl3
,
1030 Referrer(kUrl2
, blink::WebReferrerPolicyDefault
),
1031 base::string16() /* title */, ui::PAGE_TRANSITION_LINK
,
1032 false /* is_renderer_init */);
1033 host
= NavigateToEntry(manager
, entry3
);
1035 // A new RenderFrameHost should be created.
1036 EXPECT_TRUE(GetPendingFrameHost(manager
));
1037 ASSERT_EQ(host
, GetPendingFrameHost(manager
));
1039 change_observer
.Reset();
1042 manager
->DidNavigateFrame(GetPendingFrameHost(manager
), true);
1043 EXPECT_TRUE(host
== manager
->current_frame_host());
1045 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
1046 // Check the pending RenderFrameHost has been committed.
1047 EXPECT_FALSE(GetPendingFrameHost(manager
));
1049 // We should observe RVH changed event.
1050 EXPECT_TRUE(change_observer
.DidHostChange());
1053 // Tests WebUI creation.
1054 TEST_F(RenderFrameHostManagerTest
, WebUI
) {
1055 set_should_create_webui(true);
1056 SiteInstance
* instance
= SiteInstance::Create(browser_context());
1058 scoped_ptr
<TestWebContents
> web_contents(
1059 TestWebContents::Create(browser_context(), instance
));
1060 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
1061 RenderFrameHostImpl
* initial_rfh
= manager
->current_frame_host();
1063 EXPECT_FALSE(manager
->current_host()->IsRenderViewLive());
1064 EXPECT_FALSE(manager
->web_ui());
1065 EXPECT_TRUE(initial_rfh
);
1067 const GURL
kUrl("chrome://foo");
1068 NavigationEntryImpl
entry(NULL
/* instance */, -1 /* page_id */, kUrl
,
1069 Referrer(), base::string16() /* title */,
1070 ui::PAGE_TRANSITION_TYPED
,
1071 false /* is_renderer_init */);
1072 RenderFrameHostImpl
* host
= NavigateToEntry(manager
, entry
);
1074 // We commit the pending RenderFrameHost immediately because the previous
1075 // RenderFrameHost was not live. We test a case where it is live in
1078 EXPECT_NE(initial_rfh
, host
);
1079 EXPECT_EQ(host
, manager
->current_frame_host());
1080 EXPECT_FALSE(GetPendingFrameHost(manager
));
1082 // It's important that the SiteInstance get set on the Web UI page as soon
1083 // as the navigation starts, rather than lazily after it commits, so we don't
1084 // try to re-use the SiteInstance/process for non Web UI things that may
1085 // get loaded in between.
1086 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
1087 EXPECT_EQ(kUrl
, host
->GetSiteInstance()->GetSiteURL());
1089 // The Web UI is committed immediately because the RenderViewHost has not been
1090 // used yet. UpdateStateForNavigate() took the short cut path.
1091 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1092 switches::kEnableBrowserSideNavigation
)) {
1093 EXPECT_FALSE(manager
->speculative_web_ui());
1095 EXPECT_FALSE(manager
->pending_web_ui());
1097 EXPECT_TRUE(manager
->web_ui());
1100 manager
->DidNavigateFrame(host
, true);
1102 host
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1105 // Tests that we can open a WebUI link in a new tab from a WebUI page and still
1106 // grant the correct bindings. http://crbug.com/189101.
1107 TEST_F(RenderFrameHostManagerTest
, WebUIInNewTab
) {
1108 set_should_create_webui(true);
1109 SiteInstance
* blank_instance
= SiteInstance::Create(browser_context());
1110 blank_instance
->GetProcess()->Init();
1112 // Create a blank tab.
1113 scoped_ptr
<TestWebContents
> web_contents1(
1114 TestWebContents::Create(browser_context(), blank_instance
));
1115 RenderFrameHostManager
* manager1
=
1116 web_contents1
->GetRenderManagerForTesting();
1117 // Test the case that new RVH is considered live.
1118 manager1
->current_host()->CreateRenderView(-1, MSG_ROUTING_NONE
, -1,
1119 FrameReplicationState(), false);
1120 EXPECT_TRUE(manager1
->current_host()->IsRenderViewLive());
1121 EXPECT_TRUE(manager1
->current_frame_host()->IsRenderFrameLive());
1123 // Navigate to a WebUI page.
1124 const GURL
kUrl1("chrome://foo");
1125 NavigationEntryImpl
entry1(NULL
/* instance */, -1 /* page_id */, kUrl1
,
1126 Referrer(), base::string16() /* title */,
1127 ui::PAGE_TRANSITION_TYPED
,
1128 false /* is_renderer_init */);
1129 RenderFrameHostImpl
* host1
= NavigateToEntry(manager1
, entry1
);
1131 // We should have a pending navigation to the WebUI RenderViewHost.
1132 // It should already have bindings.
1133 EXPECT_EQ(host1
, GetPendingFrameHost(manager1
));
1134 EXPECT_NE(host1
, manager1
->current_frame_host());
1136 host1
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1138 // Commit and ensure we still have bindings.
1139 manager1
->DidNavigateFrame(host1
, true);
1140 SiteInstance
* webui_instance
= host1
->GetSiteInstance();
1141 EXPECT_EQ(host1
, manager1
->current_frame_host());
1143 host1
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1145 // Now simulate clicking a link that opens in a new tab.
1146 scoped_ptr
<TestWebContents
> web_contents2(
1147 TestWebContents::Create(browser_context(), webui_instance
));
1148 RenderFrameHostManager
* manager2
=
1149 web_contents2
->GetRenderManagerForTesting();
1150 // Make sure the new RVH is considered live. This is usually done in
1151 // RenderWidgetHost::Init when opening a new tab from a link.
1152 manager2
->current_host()->CreateRenderView(-1, MSG_ROUTING_NONE
, -1,
1153 FrameReplicationState(), false);
1154 EXPECT_TRUE(manager2
->current_host()->IsRenderViewLive());
1156 const GURL
kUrl2("chrome://foo/bar");
1157 NavigationEntryImpl
entry2(NULL
/* instance */, -1 /* page_id */, kUrl2
,
1158 Referrer(), base::string16() /* title */,
1159 ui::PAGE_TRANSITION_LINK
,
1160 true /* is_renderer_init */);
1161 RenderFrameHostImpl
* host2
= NavigateToEntry(manager2
, entry2
);
1163 // No cross-process transition happens because we are already in the right
1164 // SiteInstance. We should grant bindings immediately.
1165 EXPECT_EQ(host2
, manager2
->current_frame_host());
1166 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1167 switches::kEnableBrowserSideNavigation
)) {
1168 EXPECT_TRUE(manager2
->speculative_web_ui());
1170 EXPECT_TRUE(manager2
->pending_web_ui());
1173 host2
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1175 manager2
->DidNavigateFrame(host2
, true);
1178 // Tests that a WebUI is correctly reused between chrome:// pages.
1179 TEST_F(RenderFrameHostManagerTest
, WebUIWasReused
) {
1180 set_should_create_webui(true);
1182 // Navigate to a WebUI page.
1183 const GURL
kUrl1("chrome://foo");
1184 contents()->NavigateAndCommit(kUrl1
);
1185 RenderFrameHostManager
* manager
=
1186 main_test_rfh()->frame_tree_node()->render_manager();
1187 WebUIImpl
* web_ui
= manager
->web_ui();
1188 EXPECT_TRUE(web_ui
);
1190 // Navigate to another WebUI page which should be same-site and keep the
1192 const GURL
kUrl2("chrome://foo/bar");
1193 contents()->NavigateAndCommit(kUrl2
);
1194 EXPECT_EQ(web_ui
, manager
->web_ui());
1197 // Tests that a WebUI is correctly cleaned up when navigating from a chrome://
1198 // page to a non-chrome:// page.
1199 TEST_F(RenderFrameHostManagerTest
, WebUIWasCleared
) {
1200 set_should_create_webui(true);
1202 // Navigate to a WebUI page.
1203 const GURL
kUrl1("chrome://foo");
1204 contents()->NavigateAndCommit(kUrl1
);
1205 EXPECT_TRUE(main_test_rfh()->frame_tree_node()->render_manager()->web_ui());
1207 // Navigate to a non-WebUI page.
1208 const GURL
kUrl2("http://www.google.com");
1209 contents()->NavigateAndCommit(kUrl2
);
1210 EXPECT_FALSE(main_test_rfh()->frame_tree_node()->render_manager()->web_ui());
1213 // Tests that we don't end up in an inconsistent state if a page does a back and
1214 // then reload. http://crbug.com/51680
1215 // Also tests that only user-gesture navigations can interrupt cross-process
1216 // navigations. http://crbug.com/75195
1217 TEST_F(RenderFrameHostManagerTest
, PageDoesBackAndReload
) {
1218 const GURL
kUrl1("http://www.google.com/");
1219 const GURL
kUrl2("http://www.evil-site.com/");
1221 // Navigate to a safe site, then an evil site.
1222 // This will switch RenderFrameHosts. We cannot assert that the first and
1223 // second RFHs are different, though, because the first one may be promptly
1225 contents()->NavigateAndCommit(kUrl1
);
1226 contents()->NavigateAndCommit(kUrl2
);
1227 TestRenderFrameHost
* evil_rfh
= contents()->GetMainFrame();
1229 // Now let's simulate the evil page calling history.back().
1230 contents()->OnGoToEntryAtOffset(-1);
1231 contents()->GetMainFrame()->PrepareForCommit();
1232 // We should have a new pending RFH.
1233 // Note that in this case, the navigation has not committed, so evil_rfh will
1234 // not be deleted yet.
1235 EXPECT_NE(evil_rfh
, contents()->GetPendingMainFrame());
1236 EXPECT_NE(evil_rfh
->GetRenderViewHost(),
1237 contents()->GetPendingMainFrame()->GetRenderViewHost());
1239 // Before that RFH has committed, the evil page reloads itself.
1240 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1242 params
.nav_entry_id
= 0;
1243 params
.did_create_new_entry
= false;
1245 params
.transition
= ui::PAGE_TRANSITION_CLIENT_REDIRECT
;
1246 params
.should_update_history
= false;
1247 params
.gesture
= NavigationGestureAuto
;
1248 params
.was_within_same_page
= false;
1249 params
.is_post
= false;
1250 params
.page_state
= PageState::CreateFromURL(kUrl2
);
1252 contents()->GetFrameTree()->root()->navigator()->DidNavigate(evil_rfh
,
1255 // That should NOT have cancelled the pending RFH, because the reload did
1256 // not have a user gesture. Thus, the pending back navigation will still
1257 // eventually commit.
1258 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
1259 pending_render_view_host() != NULL
);
1260 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->pending_frame_host() !=
1263 contents()->GetRenderManagerForTesting()->current_frame_host());
1264 EXPECT_EQ(evil_rfh
->GetRenderViewHost(),
1265 contents()->GetRenderManagerForTesting()->current_host());
1267 // Also we should not have a pending navigation entry.
1268 EXPECT_TRUE(contents()->GetController().GetPendingEntry() == NULL
);
1269 NavigationEntry
* entry
= contents()->GetController().GetVisibleEntry();
1270 ASSERT_TRUE(entry
!= NULL
);
1271 EXPECT_EQ(kUrl2
, entry
->GetURL());
1273 // Now do the same but as a user gesture.
1274 params
.gesture
= NavigationGestureUser
;
1275 contents()->GetFrameTree()->root()->navigator()->DidNavigate(evil_rfh
,
1278 // User navigation should have cancelled the pending RFH.
1279 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
1280 pending_render_view_host() == NULL
);
1281 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->pending_frame_host() ==
1284 contents()->GetRenderManagerForTesting()->current_frame_host());
1285 EXPECT_EQ(evil_rfh
->GetRenderViewHost(),
1286 contents()->GetRenderManagerForTesting()->current_host());
1288 // Also we should not have a pending navigation entry.
1289 EXPECT_TRUE(contents()->GetController().GetPendingEntry() == NULL
);
1290 entry
= contents()->GetController().GetVisibleEntry();
1291 ASSERT_TRUE(entry
!= NULL
);
1292 EXPECT_EQ(kUrl2
, entry
->GetURL());
1295 // Ensure that we can go back and forward even if a SwapOut ACK isn't received.
1296 // See http://crbug.com/93427.
1297 TEST_F(RenderFrameHostManagerTest
, NavigateAfterMissingSwapOutACK
) {
1298 const GURL
kUrl1("http://www.google.com/");
1299 const GURL
kUrl2("http://www.chromium.org/");
1301 // Navigate to two pages.
1302 contents()->NavigateAndCommit(kUrl1
);
1303 TestRenderFrameHost
* rfh1
= main_test_rfh();
1305 // Keep active_frame_count nonzero so that no swapped out frames in
1306 // this SiteInstance get forcefully deleted.
1307 rfh1
->GetSiteInstance()->increment_active_frame_count();
1309 contents()->NavigateAndCommit(kUrl2
);
1310 TestRenderFrameHost
* rfh2
= main_test_rfh();
1311 rfh2
->GetSiteInstance()->increment_active_frame_count();
1313 // Now go back, but suppose the SwapOut_ACK isn't received. This shouldn't
1314 // happen, but we have seen it when going back quickly across many entries
1315 // (http://crbug.com/93427).
1316 contents()->GetController().GoBack();
1317 EXPECT_TRUE(rfh2
->is_waiting_for_beforeunload_ack());
1318 contents()->GetMainFrame()->PrepareForCommit();
1319 EXPECT_FALSE(rfh2
->is_waiting_for_beforeunload_ack());
1321 // The back navigation commits.
1322 const NavigationEntry
* entry1
= contents()->GetController().GetPendingEntry();
1323 contents()->GetPendingMainFrame()->SendNavigate(
1324 entry1
->GetPageID(), entry1
->GetUniqueID(), false, entry1
->GetURL());
1325 EXPECT_TRUE(rfh2
->IsWaitingForUnloadACK());
1326 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh2
->rfh_state());
1328 // We should be able to navigate forward.
1329 contents()->GetController().GoForward();
1330 contents()->GetMainFrame()->PrepareForCommit();
1331 const NavigationEntry
* entry2
= contents()->GetController().GetPendingEntry();
1332 contents()->GetPendingMainFrame()->SendNavigate(
1333 entry2
->GetPageID(), entry2
->GetUniqueID(), false, entry2
->GetURL());
1334 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, main_test_rfh()->rfh_state());
1335 if (!RenderFrameHostManager::IsSwappedOutStateForbidden()) {
1336 EXPECT_EQ(rfh2
, main_test_rfh());
1337 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
1338 rfh1
->OnSwappedOut();
1339 EXPECT_TRUE(rfh1
->is_swapped_out());
1340 EXPECT_EQ(RenderFrameHostImpl::STATE_SWAPPED_OUT
, rfh1
->rfh_state());
1344 // Test that we create swapped out RFHs for the opener chain when navigating an
1345 // opened tab cross-process. This allows us to support certain cross-process
1346 // JavaScript calls (http://crbug.com/99202).
1347 TEST_F(RenderFrameHostManagerTest
, CreateSwappedOutOpenerRFHs
) {
1348 const GURL
kUrl1("http://www.google.com/");
1349 const GURL
kUrl2("http://www.chromium.org/");
1350 const GURL
kChromeUrl("chrome://foo");
1352 // Navigate to an initial URL.
1353 contents()->NavigateAndCommit(kUrl1
);
1354 RenderFrameHostManager
* manager
= contents()->GetRenderManagerForTesting();
1355 TestRenderFrameHost
* rfh1
= main_test_rfh();
1356 scoped_refptr
<SiteInstanceImpl
> site_instance1
= rfh1
->GetSiteInstance();
1357 RenderFrameDeletedObserver
rfh1_deleted_observer(rfh1
);
1358 TestRenderViewHost
* rvh1
= test_rvh();
1360 // Create 2 new tabs and simulate them being the opener chain for the main
1361 // tab. They should be in the same SiteInstance.
1362 scoped_ptr
<TestWebContents
> opener1(
1363 TestWebContents::Create(browser_context(), site_instance1
.get()));
1364 RenderFrameHostManager
* opener1_manager
=
1365 opener1
->GetRenderManagerForTesting();
1366 contents()->SetOpener(opener1
.get());
1368 scoped_ptr
<TestWebContents
> opener2(
1369 TestWebContents::Create(browser_context(), site_instance1
.get()));
1370 RenderFrameHostManager
* opener2_manager
=
1371 opener2
->GetRenderManagerForTesting();
1372 opener1
->SetOpener(opener2
.get());
1374 // Navigate to a cross-site URL (different SiteInstance but same
1375 // BrowsingInstance).
1376 contents()->NavigateAndCommit(kUrl2
);
1377 TestRenderFrameHost
* rfh2
= main_test_rfh();
1378 TestRenderViewHost
* rvh2
= test_rvh();
1379 EXPECT_NE(site_instance1
, rfh2
->GetSiteInstance());
1380 EXPECT_TRUE(site_instance1
->IsRelatedSiteInstance(rfh2
->GetSiteInstance()));
1382 // Ensure rvh1 is placed on swapped out list of the current tab.
1383 if (!RenderFrameHostManager::IsSwappedOutStateForbidden()) {
1384 EXPECT_TRUE(manager
->IsRVHOnSwappedOutList(rvh1
));
1385 EXPECT_FALSE(rfh1_deleted_observer
.deleted());
1386 EXPECT_TRUE(manager
->IsOnSwappedOutList(rfh1
));
1388 manager
->GetRenderFrameProxyHost(site_instance1
.get())
1389 ->render_frame_host());
1391 EXPECT_TRUE(rfh1_deleted_observer
.deleted());
1392 EXPECT_TRUE(manager
->GetRenderFrameProxyHost(site_instance1
.get()));
1395 manager
->GetSwappedOutRenderViewHost(rvh1
->GetSiteInstance()));
1397 // Ensure a swapped out RFH and RFH is created in the first opener tab.
1398 RenderFrameProxyHost
* opener1_proxy
=
1399 opener1_manager
->GetRenderFrameProxyHost(rfh2
->GetSiteInstance());
1400 RenderFrameHostImpl
* opener1_rfh
= opener1_proxy
->render_frame_host();
1401 TestRenderViewHost
* opener1_rvh
= static_cast<TestRenderViewHost
*>(
1402 opener1_manager
->GetSwappedOutRenderViewHost(rvh2
->GetSiteInstance()));
1403 if (!RenderFrameHostManager::IsSwappedOutStateForbidden()) {
1404 EXPECT_TRUE(opener1_manager
->IsOnSwappedOutList(opener1_rfh
));
1405 EXPECT_TRUE(opener1_manager
->IsRVHOnSwappedOutList(opener1_rvh
));
1406 EXPECT_TRUE(opener1_rfh
->is_swapped_out());
1408 EXPECT_FALSE(opener1_rfh
);
1410 EXPECT_FALSE(opener1_rvh
->is_active());
1412 // Ensure a swapped out RFH and RVH is created in the second opener tab.
1413 RenderFrameProxyHost
* opener2_proxy
=
1414 opener2_manager
->GetRenderFrameProxyHost(rfh2
->GetSiteInstance());
1415 RenderFrameHostImpl
* opener2_rfh
= opener2_proxy
->render_frame_host();
1416 TestRenderViewHost
* opener2_rvh
= static_cast<TestRenderViewHost
*>(
1417 opener2_manager
->GetSwappedOutRenderViewHost(rvh2
->GetSiteInstance()));
1418 if (!RenderFrameHostManager::IsSwappedOutStateForbidden()) {
1419 EXPECT_TRUE(opener2_manager
->IsOnSwappedOutList(opener2_rfh
));
1420 EXPECT_TRUE(opener2_manager
->IsRVHOnSwappedOutList(opener2_rvh
));
1421 EXPECT_TRUE(opener2_rfh
->is_swapped_out());
1423 EXPECT_FALSE(opener2_rfh
);
1425 EXPECT_FALSE(opener2_rvh
->is_active());
1427 // Navigate to a cross-BrowsingInstance URL.
1428 contents()->NavigateAndCommit(kChromeUrl
);
1429 TestRenderFrameHost
* rfh3
= main_test_rfh();
1430 EXPECT_NE(site_instance1
, rfh3
->GetSiteInstance());
1431 EXPECT_FALSE(site_instance1
->IsRelatedSiteInstance(rfh3
->GetSiteInstance()));
1433 // No scripting is allowed across BrowsingInstances, so we should not create
1434 // swapped out RVHs for the opener chain in this case.
1435 EXPECT_FALSE(opener1_manager
->GetRenderFrameProxyHost(
1436 rfh3
->GetSiteInstance()));
1437 EXPECT_FALSE(opener1_manager
->GetSwappedOutRenderViewHost(
1438 rfh3
->GetSiteInstance()));
1439 EXPECT_FALSE(opener2_manager
->GetRenderFrameProxyHost(
1440 rfh3
->GetSiteInstance()));
1441 EXPECT_FALSE(opener2_manager
->GetSwappedOutRenderViewHost(
1442 rfh3
->GetSiteInstance()));
1445 // Test that a page can disown the opener of the WebContents.
1446 TEST_F(RenderFrameHostManagerTest
, DisownOpener
) {
1447 const GURL
kUrl1("http://www.google.com/");
1448 const GURL
kUrl2("http://www.chromium.org/");
1450 // Navigate to an initial URL.
1451 contents()->NavigateAndCommit(kUrl1
);
1452 TestRenderFrameHost
* rfh1
= main_test_rfh();
1453 scoped_refptr
<SiteInstanceImpl
> site_instance1
= rfh1
->GetSiteInstance();
1455 // Create a new tab and simulate having it be the opener for the main tab.
1456 scoped_ptr
<TestWebContents
> opener1(
1457 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1458 contents()->SetOpener(opener1
.get());
1459 EXPECT_TRUE(contents()->HasOpener());
1461 // Navigate to a cross-site URL (different SiteInstance but same
1462 // BrowsingInstance).
1463 contents()->NavigateAndCommit(kUrl2
);
1464 TestRenderFrameHost
* rfh2
= main_test_rfh();
1465 EXPECT_NE(site_instance1
, rfh2
->GetSiteInstance());
1467 // Disown the opener from rfh2.
1468 rfh2
->DidDisownOpener();
1470 // Ensure the opener is cleared.
1471 EXPECT_FALSE(contents()->HasOpener());
1474 // Test that a page can disown a same-site opener of the WebContents.
1475 TEST_F(RenderFrameHostManagerTest
, DisownSameSiteOpener
) {
1476 const GURL
kUrl1("http://www.google.com/");
1478 // Navigate to an initial URL.
1479 contents()->NavigateAndCommit(kUrl1
);
1480 TestRenderFrameHost
* rfh1
= main_test_rfh();
1482 // Create a new tab and simulate having it be the opener for the main tab.
1483 scoped_ptr
<TestWebContents
> opener1(
1484 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1485 contents()->SetOpener(opener1
.get());
1486 EXPECT_TRUE(contents()->HasOpener());
1488 // Disown the opener from rfh1.
1489 rfh1
->DidDisownOpener();
1491 // Ensure the opener is cleared even if it is in the same process.
1492 EXPECT_FALSE(contents()->HasOpener());
1495 // Test that a page can disown the opener just as a cross-process navigation is
1497 TEST_F(RenderFrameHostManagerTest
, DisownOpenerDuringNavigation
) {
1498 const GURL
kUrl1("http://www.google.com/");
1499 const GURL
kUrl2("http://www.chromium.org/");
1501 // Navigate to an initial URL.
1502 contents()->NavigateAndCommit(kUrl1
);
1503 scoped_refptr
<SiteInstanceImpl
> site_instance1
=
1504 main_test_rfh()->GetSiteInstance();
1506 // Create a new tab and simulate having it be the opener for the main tab.
1507 scoped_ptr
<TestWebContents
> opener1(
1508 TestWebContents::Create(browser_context(), site_instance1
.get()));
1509 contents()->SetOpener(opener1
.get());
1510 EXPECT_TRUE(contents()->HasOpener());
1512 // Navigate to a cross-site URL (different SiteInstance but same
1513 // BrowsingInstance).
1514 contents()->NavigateAndCommit(kUrl2
);
1515 TestRenderFrameHost
* rfh2
= main_test_rfh();
1516 EXPECT_NE(site_instance1
, rfh2
->GetSiteInstance());
1518 // Start a back navigation.
1519 contents()->GetController().GoBack();
1520 contents()->GetMainFrame()->PrepareForCommit();
1522 // Disown the opener from rfh2.
1523 rfh2
->DidDisownOpener();
1525 // Ensure the opener is cleared.
1526 EXPECT_FALSE(contents()->HasOpener());
1528 // The back navigation commits.
1529 const NavigationEntry
* entry1
= contents()->GetController().GetPendingEntry();
1530 contents()->GetPendingMainFrame()->SendNavigate(
1531 entry1
->GetPageID(), entry1
->GetUniqueID(), false, entry1
->GetURL());
1533 // Ensure the opener is still cleared.
1534 EXPECT_FALSE(contents()->HasOpener());
1537 // Test that a page can disown the opener just after a cross-process navigation
1539 TEST_F(RenderFrameHostManagerTest
, DisownOpenerAfterNavigation
) {
1540 const GURL
kUrl1("http://www.google.com/");
1541 const GURL
kUrl2("http://www.chromium.org/");
1543 // Navigate to an initial URL.
1544 contents()->NavigateAndCommit(kUrl1
);
1545 scoped_refptr
<SiteInstanceImpl
> site_instance1
=
1546 main_test_rfh()->GetSiteInstance();
1548 // Create a new tab and simulate having it be the opener for the main tab.
1549 scoped_ptr
<TestWebContents
> opener1(
1550 TestWebContents::Create(browser_context(), site_instance1
.get()));
1551 contents()->SetOpener(opener1
.get());
1552 EXPECT_TRUE(contents()->HasOpener());
1554 // Navigate to a cross-site URL (different SiteInstance but same
1555 // BrowsingInstance).
1556 contents()->NavigateAndCommit(kUrl2
);
1557 TestRenderFrameHost
* rfh2
= main_test_rfh();
1558 EXPECT_NE(site_instance1
, rfh2
->GetSiteInstance());
1560 // Commit a back navigation before the DidDisownOpener message arrives.
1561 contents()->GetController().GoBack();
1562 contents()->GetMainFrame()->PrepareForCommit();
1563 const NavigationEntry
* entry1
= contents()->GetController().GetPendingEntry();
1564 contents()->GetPendingMainFrame()->SendNavigate(
1565 entry1
->GetPageID(), entry1
->GetUniqueID(), false, entry1
->GetURL());
1567 // Disown the opener from rfh2.
1568 rfh2
->DidDisownOpener();
1569 EXPECT_FALSE(contents()->HasOpener());
1572 // Test that we clean up swapped out RenderViewHosts when a process hosting
1573 // those associated RenderViews crashes. http://crbug.com/258993
1574 TEST_F(RenderFrameHostManagerTest
, CleanUpSwappedOutRVHOnProcessCrash
) {
1575 const GURL
kUrl1("http://www.google.com/");
1576 const GURL
kUrl2("http://www.chromium.org/");
1578 // Navigate to an initial URL.
1579 contents()->NavigateAndCommit(kUrl1
);
1580 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1582 // Create a new tab as an opener for the main tab.
1583 scoped_ptr
<TestWebContents
> opener1(
1584 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1585 RenderFrameHostManager
* opener1_manager
=
1586 opener1
->GetRenderManagerForTesting();
1587 contents()->SetOpener(opener1
.get());
1589 // Make sure the new opener RVH is considered live.
1590 opener1_manager
->current_host()->CreateRenderView(
1591 -1, MSG_ROUTING_NONE
, -1, FrameReplicationState(), false);
1592 EXPECT_TRUE(opener1_manager
->current_host()->IsRenderViewLive());
1593 EXPECT_TRUE(opener1_manager
->current_frame_host()->IsRenderFrameLive());
1595 // Use a cross-process navigation in the opener to swap out the old RVH.
1597 opener1_manager
->GetSwappedOutRenderViewHost(rfh1
->GetSiteInstance()));
1598 opener1
->NavigateAndCommit(kUrl2
);
1599 RenderViewHostImpl
* swapped_out_rvh
=
1600 opener1_manager
->GetSwappedOutRenderViewHost(rfh1
->GetSiteInstance());
1601 EXPECT_TRUE(swapped_out_rvh
);
1602 EXPECT_TRUE(swapped_out_rvh
->is_swapped_out_
);
1603 EXPECT_FALSE(swapped_out_rvh
->is_active());
1605 // Fake a process crash.
1606 rfh1
->GetProcess()->SimulateCrash();
1608 // Ensure that the RenderFrameProxyHost stays around and the RenderFrameProxy
1610 RenderFrameProxyHost
* render_frame_proxy_host
=
1611 opener1_manager
->GetRenderFrameProxyHost(rfh1
->GetSiteInstance());
1612 EXPECT_TRUE(render_frame_proxy_host
);
1613 EXPECT_FALSE(render_frame_proxy_host
->is_render_frame_proxy_live());
1615 // Expect the swapped out RVH to exist but not be live.
1617 opener1_manager
->GetSwappedOutRenderViewHost(rfh1
->GetSiteInstance()));
1619 opener1_manager
->GetSwappedOutRenderViewHost(rfh1
->GetSiteInstance())
1620 ->IsRenderViewLive());
1622 // Reload the initial tab. This should recreate the opener's swapped out RVH
1623 // in the original SiteInstance.
1624 contents()->GetController().Reload(true);
1625 contents()->GetMainFrame()->PrepareForCommit();
1627 opener1_manager
->GetSwappedOutRenderViewHost(rfh1
->GetSiteInstance())
1628 ->IsRenderViewLive());
1630 opener1_manager
->GetRoutingIdForSiteInstance(rfh1
->GetSiteInstance()),
1631 contents()->GetMainFrame()->GetRenderViewHost()->opener_frame_route_id());
1634 // Test that RenderViewHosts created for WebUI navigations are properly
1635 // granted WebUI bindings even if an unprivileged swapped out RenderViewHost
1636 // is in the same process (http://crbug.com/79918).
1637 TEST_F(RenderFrameHostManagerTest
, EnableWebUIWithSwappedOutOpener
) {
1638 set_should_create_webui(true);
1639 const GURL
kSettingsUrl("chrome://chrome/settings");
1640 const GURL
kPluginUrl("chrome://plugins");
1642 // Navigate to an initial WebUI URL.
1643 contents()->NavigateAndCommit(kSettingsUrl
);
1645 // Ensure the RVH has WebUI bindings.
1646 TestRenderViewHost
* rvh1
= test_rvh();
1647 EXPECT_TRUE(rvh1
->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1649 // Create a new tab and simulate it being the opener for the main
1650 // tab. It should be in the same SiteInstance.
1651 scoped_ptr
<TestWebContents
> opener1(
1652 TestWebContents::Create(browser_context(), rvh1
->GetSiteInstance()));
1653 RenderFrameHostManager
* opener1_manager
=
1654 opener1
->GetRenderManagerForTesting();
1655 contents()->SetOpener(opener1
.get());
1657 // Navigate to a different WebUI URL (different SiteInstance, same
1658 // BrowsingInstance).
1659 contents()->NavigateAndCommit(kPluginUrl
);
1660 TestRenderViewHost
* rvh2
= test_rvh();
1661 EXPECT_NE(rvh1
->GetSiteInstance(), rvh2
->GetSiteInstance());
1662 EXPECT_TRUE(rvh1
->GetSiteInstance()->IsRelatedSiteInstance(
1663 rvh2
->GetSiteInstance()));
1665 // Ensure a swapped out RFH and RVH is created in the first opener tab.
1666 RenderFrameProxyHost
* opener1_proxy
=
1667 opener1_manager
->GetRenderFrameProxyHost(rvh2
->GetSiteInstance());
1668 RenderFrameHostImpl
* opener1_rfh
= opener1_proxy
->render_frame_host();
1669 TestRenderViewHost
* opener1_rvh
= static_cast<TestRenderViewHost
*>(
1670 opener1_manager
->GetSwappedOutRenderViewHost(rvh2
->GetSiteInstance()));
1671 if (!RenderFrameHostManager::IsSwappedOutStateForbidden()) {
1672 EXPECT_TRUE(opener1_manager
->IsOnSwappedOutList(opener1_rfh
));
1673 EXPECT_TRUE(opener1_manager
->IsRVHOnSwappedOutList(opener1_rvh
));
1674 EXPECT_TRUE(opener1_rfh
->is_swapped_out());
1676 EXPECT_FALSE(opener1_rfh
);
1678 EXPECT_FALSE(opener1_rvh
->is_active());
1680 // Ensure the new RVH has WebUI bindings.
1681 EXPECT_TRUE(rvh2
->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1684 // Test that we reuse the same guest SiteInstance if we navigate across sites.
1685 TEST_F(RenderFrameHostManagerTest
, NoSwapOnGuestNavigations
) {
1686 GURL
guest_url(std::string(kGuestScheme
).append("://abc123"));
1687 SiteInstance
* instance
=
1688 SiteInstance::CreateForURL(browser_context(), guest_url
);
1689 scoped_ptr
<TestWebContents
> web_contents(
1690 TestWebContents::Create(browser_context(), instance
));
1692 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
1694 RenderFrameHostImpl
* host
= NULL
;
1696 // 1) The first navigation. --------------------------
1697 const GURL
kUrl1("http://www.google.com/");
1698 NavigationEntryImpl
entry1(
1699 NULL
/* instance */, -1 /* page_id */, kUrl1
, Referrer(),
1700 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED
,
1701 false /* is_renderer_init */);
1702 host
= NavigateToEntry(manager
, entry1
);
1704 // The RenderFrameHost created in Init will be reused.
1705 EXPECT_TRUE(host
== manager
->current_frame_host());
1706 EXPECT_FALSE(manager
->pending_frame_host());
1707 EXPECT_EQ(manager
->current_frame_host()->GetSiteInstance(), instance
);
1710 manager
->DidNavigateFrame(host
, true);
1711 // Commit to SiteInstance should be delayed until RenderFrame commit.
1712 EXPECT_EQ(host
, manager
->current_frame_host());
1714 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
1716 // 2) Navigate to a different domain. -------------------------
1717 // Guests stay in the same process on navigation.
1718 const GURL
kUrl2("http://www.chromium.org");
1719 NavigationEntryImpl
entry2(
1720 NULL
/* instance */, -1 /* page_id */, kUrl2
,
1721 Referrer(kUrl1
, blink::WebReferrerPolicyDefault
),
1722 base::string16() /* title */, ui::PAGE_TRANSITION_LINK
,
1723 true /* is_renderer_init */);
1724 host
= NavigateToEntry(manager
, entry2
);
1726 // The RenderFrameHost created in Init will be reused.
1727 EXPECT_EQ(host
, manager
->current_frame_host());
1728 EXPECT_FALSE(manager
->pending_frame_host());
1731 manager
->DidNavigateFrame(host
, true);
1732 EXPECT_EQ(host
, manager
->current_frame_host());
1734 EXPECT_EQ(host
->GetSiteInstance(), instance
);
1737 // Test that we cancel a pending RVH if we close the tab while it's pending.
1738 // http://crbug.com/294697.
1739 TEST_F(RenderFrameHostManagerTest
, NavigateWithEarlyClose
) {
1740 TestNotificationTracker notifications
;
1742 SiteInstance
* instance
= SiteInstance::Create(browser_context());
1744 BeforeUnloadFiredWebContentsDelegate delegate
;
1745 scoped_ptr
<TestWebContents
> web_contents(
1746 TestWebContents::Create(browser_context(), instance
));
1747 RenderViewHostChangedObserver
change_observer(web_contents
.get());
1748 web_contents
->SetDelegate(&delegate
);
1750 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
1752 // 1) The first navigation. --------------------------
1753 const GURL
kUrl1("http://www.google.com/");
1754 NavigationEntryImpl
entry1(NULL
/* instance */, -1 /* page_id */, kUrl1
,
1755 Referrer(), base::string16() /* title */,
1756 ui::PAGE_TRANSITION_TYPED
,
1757 false /* is_renderer_init */);
1758 RenderFrameHostImpl
* host
= NavigateToEntry(manager
, entry1
);
1760 // The RenderFrameHost created in Init will be reused.
1761 EXPECT_EQ(host
, manager
->current_frame_host());
1762 EXPECT_FALSE(GetPendingFrameHost(manager
));
1764 // We should observe RVH changed event.
1765 EXPECT_TRUE(change_observer
.DidHostChange());
1768 manager
->DidNavigateFrame(host
, true);
1770 // Commit to SiteInstance should be delayed until RenderFrame commits.
1771 EXPECT_EQ(host
, manager
->current_frame_host());
1772 EXPECT_FALSE(host
->GetSiteInstance()->HasSite());
1773 host
->GetSiteInstance()->SetSite(kUrl1
);
1775 // 2) Cross-site navigate to next site. -------------------------
1776 const GURL
kUrl2("http://www.example.com");
1777 NavigationEntryImpl
entry2(
1778 NULL
/* instance */, -1 /* page_id */, kUrl2
, Referrer(),
1779 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED
,
1780 false /* is_renderer_init */);
1781 RenderFrameHostImpl
* host2
= NavigateToEntry(manager
, entry2
);
1783 // A new RenderFrameHost should be created.
1784 ASSERT_EQ(host2
, GetPendingFrameHost(manager
));
1785 EXPECT_NE(host2
, host
);
1787 EXPECT_EQ(host
, manager
->current_frame_host());
1788 EXPECT_FALSE(manager
->current_frame_host()->is_swapped_out());
1789 EXPECT_EQ(host2
, GetPendingFrameHost(manager
));
1791 // 3) Close the tab. -------------------------
1792 notifications
.ListenFor(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED
,
1793 Source
<RenderWidgetHost
>(host2
->render_view_host()));
1794 manager
->OnBeforeUnloadACK(false, true, base::TimeTicks());
1797 notifications
.Check1AndReset(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED
));
1798 EXPECT_FALSE(GetPendingFrameHost(manager
));
1799 EXPECT_EQ(host
, manager
->current_frame_host());
1802 TEST_F(RenderFrameHostManagerTest
, CloseWithPendingWhileUnresponsive
) {
1803 const GURL
kUrl1("http://www.google.com/");
1804 const GURL
kUrl2("http://www.chromium.org/");
1806 CloseWebContentsDelegate close_delegate
;
1807 contents()->SetDelegate(&close_delegate
);
1809 // Navigate to the first page.
1810 contents()->NavigateAndCommit(kUrl1
);
1811 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1813 // Start to close the tab, but assume it's unresponsive.
1814 rfh1
->render_view_host()->ClosePage();
1815 EXPECT_TRUE(rfh1
->render_view_host()->is_waiting_for_close_ack());
1817 // Start a navigation to a new site.
1818 controller().LoadURL(
1819 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1820 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1821 switches::kEnableBrowserSideNavigation
)) {
1822 rfh1
->PrepareForCommit();
1824 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1826 // Simulate the unresponsiveness timer. The tab should close.
1827 contents()->RendererUnresponsive(rfh1
->render_view_host());
1828 EXPECT_TRUE(close_delegate
.is_closed());
1831 // Tests that the RenderFrameHost is properly deleted when the SwapOutACK is
1832 // received. (SwapOut and the corresponding ACK always occur after commit.)
1833 // Also tests that an early SwapOutACK is properly ignored.
1834 TEST_F(RenderFrameHostManagerTest
, DeleteFrameAfterSwapOutACK
) {
1835 const GURL
kUrl1("http://www.google.com/");
1836 const GURL
kUrl2("http://www.chromium.org/");
1838 // Navigate to the first page.
1839 contents()->NavigateAndCommit(kUrl1
);
1840 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1841 RenderFrameDeletedObserver
rfh_deleted_observer(rfh1
);
1842 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1844 // Navigate to new site, simulating onbeforeunload approval.
1845 controller().LoadURL(
1846 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1847 int entry_id
= controller().GetPendingEntry()->GetUniqueID();
1848 contents()->GetMainFrame()->PrepareForCommit();
1849 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1850 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1851 TestRenderFrameHost
* rfh2
= contents()->GetPendingMainFrame();
1853 // Simulate the swap out ack, unexpectedly early (before commit). It should
1855 rfh1
->OnSwappedOut();
1856 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1857 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1859 // The new page commits.
1860 contents()->TestDidNavigate(rfh2
, 1, entry_id
, true, kUrl2
,
1861 ui::PAGE_TRANSITION_TYPED
);
1862 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1863 EXPECT_EQ(rfh2
, contents()->GetMainFrame());
1864 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL
);
1865 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
1866 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
1868 rfh1
->frame_tree_node()->render_manager()->IsPendingDeletion(rfh1
));
1870 // Simulate the swap out ack.
1871 rfh1
->OnSwappedOut();
1873 // rfh1 should have been deleted.
1874 EXPECT_TRUE(rfh_deleted_observer
.deleted());
1878 // Tests that the RenderFrameHost is properly swapped out when the SwapOut ACK
1879 // is received. (SwapOut and the corresponding ACK always occur after commit.)
1880 TEST_F(RenderFrameHostManagerTest
, SwapOutFrameAfterSwapOutACK
) {
1881 const GURL
kUrl1("http://www.google.com/");
1882 const GURL
kUrl2("http://www.chromium.org/");
1884 // Navigate to the first page.
1885 contents()->NavigateAndCommit(kUrl1
);
1886 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1887 RenderFrameDeletedObserver
rfh_deleted_observer(rfh1
);
1888 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1890 // Increment the number of active frames in SiteInstanceImpl so that rfh1 is
1891 // not deleted on swap out.
1892 rfh1
->GetSiteInstance()->increment_active_frame_count();
1894 // Navigate to new site, simulating onbeforeunload approval.
1895 controller().LoadURL(
1896 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1897 int entry_id
= controller().GetPendingEntry()->GetUniqueID();
1898 contents()->GetMainFrame()->PrepareForCommit();
1899 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1900 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1901 TestRenderFrameHost
* rfh2
= contents()->GetPendingMainFrame();
1903 // The new page commits.
1904 contents()->TestDidNavigate(rfh2
, 1, entry_id
, true, kUrl2
,
1905 ui::PAGE_TRANSITION_TYPED
);
1906 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1907 EXPECT_EQ(rfh2
, contents()->GetMainFrame());
1908 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL
);
1909 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
1910 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
1912 // Simulate the swap out ack.
1913 rfh1
->OnSwappedOut();
1915 // rfh1 should be swapped out or deleted in --site-per-process.
1916 if (!RenderFrameHostManager::IsSwappedOutStateForbidden()) {
1917 EXPECT_FALSE(rfh_deleted_observer
.deleted());
1918 EXPECT_TRUE(rfh1
->is_swapped_out());
1920 EXPECT_TRUE(rfh_deleted_observer
.deleted());
1924 // Test that the RenderViewHost is properly swapped out if a navigation in the
1925 // new renderer commits before sending the SwapOut message to the old renderer.
1926 // This simulates a cross-site navigation to a synchronously committing URL
1927 // (e.g., a data URL) and ensures it works properly.
1928 TEST_F(RenderFrameHostManagerTest
,
1929 CommitNewNavigationBeforeSendingSwapOut
) {
1930 const GURL
kUrl1("http://www.google.com/");
1931 const GURL
kUrl2("http://www.chromium.org/");
1933 // Navigate to the first page.
1934 contents()->NavigateAndCommit(kUrl1
);
1935 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1936 RenderFrameDeletedObserver
rfh_deleted_observer(rfh1
);
1937 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1939 // Increment the number of active frames in SiteInstanceImpl so that rfh1 is
1940 // not deleted on swap out.
1941 scoped_refptr
<SiteInstanceImpl
> site_instance
= rfh1
->GetSiteInstance();
1942 site_instance
->increment_active_frame_count();
1944 // Navigate to new site, simulating onbeforeunload approval.
1945 controller().LoadURL(
1946 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1947 int entry_id
= controller().GetPendingEntry()->GetUniqueID();
1948 rfh1
->PrepareForCommit();
1949 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1950 TestRenderFrameHost
* rfh2
= contents()->GetPendingMainFrame();
1952 // The new page commits.
1953 contents()->TestDidNavigate(rfh2
, 1, entry_id
, true, kUrl2
,
1954 ui::PAGE_TRANSITION_TYPED
);
1955 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1956 EXPECT_EQ(rfh2
, contents()->GetMainFrame());
1957 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL
);
1958 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
1959 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
1961 // Simulate the swap out ack.
1962 rfh1
->OnSwappedOut();
1964 // rfh1 should be swapped out.
1965 if (RenderFrameHostManager::IsSwappedOutStateForbidden()) {
1966 EXPECT_TRUE(rfh_deleted_observer
.deleted());
1967 EXPECT_TRUE(contents()->GetFrameTree()->root()->render_manager()
1968 ->GetRenderFrameProxyHost(site_instance
.get()));
1970 EXPECT_FALSE(rfh_deleted_observer
.deleted());
1971 EXPECT_TRUE(rfh1
->is_swapped_out());
1975 // Test that a RenderFrameHost is properly deleted when a cross-site navigation
1977 TEST_F(RenderFrameHostManagerTest
,
1978 CancelPendingProperlyDeletesOrSwaps
) {
1979 const GURL
kUrl1("http://www.google.com/");
1980 const GURL
kUrl2("http://www.chromium.org/");
1981 RenderFrameHostImpl
* pending_rfh
= NULL
;
1982 base::TimeTicks now
= base::TimeTicks::Now();
1984 // Navigate to the first page.
1985 contents()->NavigateAndCommit(kUrl1
);
1986 TestRenderFrameHost
* rfh1
= main_test_rfh();
1987 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1989 // Navigate to a new site, starting a cross-site navigation.
1990 controller().LoadURL(
1991 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1993 pending_rfh
= contents()->GetPendingMainFrame();
1994 RenderFrameDeletedObserver
rfh_deleted_observer(pending_rfh
);
1996 // Cancel the navigation by simulating a declined beforeunload dialog.
1997 contents()->GetMainFrame()->OnMessageReceived(
1998 FrameHostMsg_BeforeUnload_ACK(0, false, now
, now
));
1999 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
2001 // Since the pending RFH is the only one for the new SiteInstance, it should
2003 EXPECT_TRUE(rfh_deleted_observer
.deleted());
2006 // Start another cross-site navigation.
2007 controller().LoadURL(
2008 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
2010 pending_rfh
= contents()->GetPendingMainFrame();
2011 RenderFrameDeletedObserver
rfh_deleted_observer(pending_rfh
);
2013 // Increment the number of active frames in the new SiteInstance, which will
2014 // cause the pending RFH to be deleted and a RenderFrameProxyHost to be
2016 scoped_refptr
<SiteInstanceImpl
> site_instance
=
2017 pending_rfh
->GetSiteInstance();
2018 site_instance
->increment_active_frame_count();
2020 contents()->GetMainFrame()->OnMessageReceived(
2021 FrameHostMsg_BeforeUnload_ACK(0, false, now
, now
));
2022 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
2024 if (RenderFrameHostManager::IsSwappedOutStateForbidden()) {
2025 EXPECT_TRUE(rfh_deleted_observer
.deleted());
2026 EXPECT_TRUE(contents()->GetFrameTree()->root()->render_manager()
2027 ->GetRenderFrameProxyHost(site_instance
.get()));
2029 EXPECT_FALSE(rfh_deleted_observer
.deleted());
2034 // Test that a pending RenderFrameHost in a non-root frame tree node is properly
2035 // deleted when the node is detached. Motivated by http://crbug.com/441357 and
2036 // http://crbug.com/444955.
2037 TEST_F(RenderFrameHostManagerTest
, DetachPendingChild
) {
2038 base::CommandLine::ForCurrentProcess()->AppendSwitch(
2039 switches::kSitePerProcess
);
2041 const GURL
kUrlA("http://www.google.com/");
2042 const GURL
kUrlB("http://webkit.org/");
2044 // Create a page with two child frames.
2045 contents()->NavigateAndCommit(kUrlA
);
2046 contents()->GetMainFrame()->OnCreateChildFrame(
2047 contents()->GetMainFrame()->GetProcess()->GetNextRoutingID(),
2048 blink::WebTreeScopeType::Document
, "frame_name",
2049 blink::WebSandboxFlags::None
);
2050 contents()->GetMainFrame()->OnCreateChildFrame(
2051 contents()->GetMainFrame()->GetProcess()->GetNextRoutingID(),
2052 blink::WebTreeScopeType::Document
, "frame_name",
2053 blink::WebSandboxFlags::None
);
2054 RenderFrameHostManager
* root_manager
=
2055 contents()->GetFrameTree()->root()->render_manager();
2056 RenderFrameHostManager
* iframe1
=
2057 contents()->GetFrameTree()->root()->child_at(0)->render_manager();
2058 RenderFrameHostManager
* iframe2
=
2059 contents()->GetFrameTree()->root()->child_at(1)->render_manager();
2061 // 1) The first navigation.
2062 NavigationEntryImpl
entryA(NULL
/* instance */, -1 /* page_id */, kUrlA
,
2063 Referrer(), base::string16() /* title */,
2064 ui::PAGE_TRANSITION_TYPED
,
2065 false /* is_renderer_init */);
2066 RenderFrameHostImpl
* host1
= NavigateToEntry(iframe1
, entryA
);
2068 // The RenderFrameHost created in Init will be reused.
2069 EXPECT_TRUE(host1
== iframe1
->current_frame_host());
2070 EXPECT_FALSE(GetPendingFrameHost(iframe1
));
2073 iframe1
->DidNavigateFrame(host1
, true);
2074 // Commit to SiteInstance should be delayed until RenderFrame commit.
2075 EXPECT_TRUE(host1
== iframe1
->current_frame_host());
2077 EXPECT_TRUE(host1
->GetSiteInstance()->HasSite());
2079 // 2) Cross-site navigate both frames to next site.
2080 NavigationEntryImpl
entryB(NULL
/* instance */, -1 /* page_id */, kUrlB
,
2081 Referrer(kUrlA
, blink::WebReferrerPolicyDefault
),
2082 base::string16() /* title */,
2083 ui::PAGE_TRANSITION_LINK
,
2084 false /* is_renderer_init */);
2085 host1
= NavigateToEntry(iframe1
, entryB
);
2086 RenderFrameHostImpl
* host2
= NavigateToEntry(iframe2
, entryB
);
2088 // A new, pending RenderFrameHost should be created in each FrameTreeNode.
2089 EXPECT_TRUE(GetPendingFrameHost(iframe1
));
2090 EXPECT_TRUE(GetPendingFrameHost(iframe2
));
2091 EXPECT_EQ(host1
, GetPendingFrameHost(iframe1
));
2092 EXPECT_EQ(host2
, GetPendingFrameHost(iframe2
));
2093 EXPECT_TRUE(RenderFrameHostImpl::IsRFHStateActive(
2094 GetPendingFrameHost(iframe1
)->rfh_state()));
2095 EXPECT_TRUE(RenderFrameHostImpl::IsRFHStateActive(
2096 GetPendingFrameHost(iframe2
)->rfh_state()));
2097 EXPECT_NE(GetPendingFrameHost(iframe1
), GetPendingFrameHost(iframe2
));
2098 EXPECT_EQ(GetPendingFrameHost(iframe1
)->GetSiteInstance(),
2099 GetPendingFrameHost(iframe2
)->GetSiteInstance());
2100 EXPECT_NE(iframe1
->current_frame_host(), GetPendingFrameHost(iframe1
));
2101 EXPECT_NE(iframe2
->current_frame_host(), GetPendingFrameHost(iframe2
));
2102 EXPECT_FALSE(contents()->CrossProcessNavigationPending())
2103 << "There should be no top-level pending navigation.";
2105 RenderFrameDeletedObserver
delete_watcher1(GetPendingFrameHost(iframe1
));
2106 RenderFrameDeletedObserver
delete_watcher2(GetPendingFrameHost(iframe2
));
2107 EXPECT_FALSE(delete_watcher1
.deleted());
2108 EXPECT_FALSE(delete_watcher2
.deleted());
2110 // Keep the SiteInstance alive for testing.
2111 scoped_refptr
<SiteInstanceImpl
> site_instance
=
2112 GetPendingFrameHost(iframe1
)->GetSiteInstance();
2113 EXPECT_TRUE(site_instance
->HasSite());
2114 EXPECT_NE(site_instance
, contents()->GetSiteInstance());
2115 EXPECT_EQ(2U, site_instance
->active_frame_count());
2117 // Proxies should exist.
2119 root_manager
->GetRenderFrameProxyHost(site_instance
.get()));
2121 iframe1
->GetRenderFrameProxyHost(site_instance
.get()));
2123 iframe2
->GetRenderFrameProxyHost(site_instance
.get()));
2125 // Detach the first child FrameTreeNode. This should kill the pending host but
2126 // not yet destroy proxies in |site_instance| since the other child remains.
2127 iframe1
->current_frame_host()->OnMessageReceived(
2128 FrameHostMsg_Detach(iframe1
->current_frame_host()->GetRoutingID()));
2129 iframe1
= NULL
; // Was just destroyed.
2131 EXPECT_TRUE(delete_watcher1
.deleted());
2132 EXPECT_FALSE(delete_watcher2
.deleted());
2133 EXPECT_EQ(1U, site_instance
->active_frame_count());
2135 // Proxies should still exist.
2137 root_manager
->GetRenderFrameProxyHost(site_instance
.get()));
2139 iframe2
->GetRenderFrameProxyHost(site_instance
.get()));
2141 // Detach the second child FrameTreeNode. This should trigger cleanup of
2142 // RenderFrameProxyHosts in |site_instance|.
2143 iframe2
->current_frame_host()->OnMessageReceived(
2144 FrameHostMsg_Detach(iframe2
->current_frame_host()->GetRoutingID()));
2145 iframe2
= NULL
; // Was just destroyed.
2147 EXPECT_TRUE(delete_watcher1
.deleted());
2148 EXPECT_TRUE(delete_watcher2
.deleted());
2150 EXPECT_EQ(0U, site_instance
->active_frame_count());
2152 root_manager
->GetRenderFrameProxyHost(site_instance
.get()))
2153 << "Proxies should have been cleaned up";
2154 EXPECT_TRUE(site_instance
->HasOneRef())
2155 << "This SiteInstance should be destroyable now.";
2158 // Two tabs in the same process crash. The first tab is reloaded, and the second
2159 // tab navigates away without reloading. The second tab's navigation shouldn't
2160 // mess with the first tab's content. Motivated by http://crbug.com/473714.
2161 TEST_F(RenderFrameHostManagerTest
, TwoTabsCrashOneReloadsOneLeaves
) {
2162 base::CommandLine::ForCurrentProcess()->AppendSwitch(
2163 switches::kSitePerProcess
);
2165 const GURL
kUrl1("http://www.google.com/");
2166 const GURL
kUrl2("http://webkit.org/");
2167 const GURL
kUrl3("http://whatwg.org/");
2169 // |contents1| and |contents2| navigate to the same page and then crash.
2170 TestWebContents
* contents1
= contents();
2171 scoped_ptr
<TestWebContents
> contents2(
2172 TestWebContents::Create(browser_context(), contents1
->GetSiteInstance()));
2173 contents1
->NavigateAndCommit(kUrl1
);
2174 contents2
->NavigateAndCommit(kUrl1
);
2175 MockRenderProcessHost
* rph
= contents1
->GetMainFrame()->GetProcess();
2176 EXPECT_EQ(rph
, contents2
->GetMainFrame()->GetProcess());
2177 rph
->SimulateCrash();
2178 EXPECT_FALSE(contents1
->GetMainFrame()->IsRenderFrameLive());
2179 EXPECT_FALSE(contents2
->GetMainFrame()->IsRenderFrameLive());
2180 EXPECT_EQ(contents1
->GetSiteInstance(), contents2
->GetSiteInstance());
2182 // Reload |contents1|.
2183 contents1
->NavigateAndCommit(kUrl1
);
2184 EXPECT_TRUE(contents1
->GetMainFrame()->IsRenderFrameLive());
2185 EXPECT_FALSE(contents2
->GetMainFrame()->IsRenderFrameLive());
2186 EXPECT_EQ(contents1
->GetSiteInstance(), contents2
->GetSiteInstance());
2188 // |contents1| creates an out of process iframe.
2189 contents1
->GetMainFrame()->OnCreateChildFrame(
2190 contents1
->GetMainFrame()->GetProcess()->GetNextRoutingID(),
2191 blink::WebTreeScopeType::Document
, "frame_name",
2192 blink::WebSandboxFlags::None
);
2193 RenderFrameHostManager
* iframe
=
2194 contents()->GetFrameTree()->root()->child_at(0)->render_manager();
2195 NavigationEntryImpl
entry(NULL
/* instance */, -1 /* page_id */, kUrl2
,
2196 Referrer(kUrl1
, blink::WebReferrerPolicyDefault
),
2197 base::string16() /* title */,
2198 ui::PAGE_TRANSITION_LINK
,
2199 false /* is_renderer_init */);
2200 RenderFrameHostImpl
* cross_site
= NavigateToEntry(iframe
, entry
);
2201 iframe
->DidNavigateFrame(cross_site
, true);
2203 // A proxy to the iframe should now exist in the SiteInstance of the main
2205 EXPECT_NE(cross_site
->GetSiteInstance(), contents1
->GetSiteInstance());
2207 iframe
->GetRenderFrameProxyHost(contents1
->GetSiteInstance()));
2209 iframe
->GetRenderFrameProxyHost(contents2
->GetSiteInstance()));
2211 // Navigate |contents2| away from the sad tab (and thus away from the
2212 // SiteInstance of |contents1|). This should not destroy the proxies needed by
2213 // |contents1| -- that was http://crbug.com/473714.
2214 EXPECT_FALSE(contents2
->GetMainFrame()->IsRenderFrameLive());
2215 contents2
->NavigateAndCommit(kUrl3
);
2216 EXPECT_TRUE(contents2
->GetMainFrame()->IsRenderFrameLive());
2218 iframe
->GetRenderFrameProxyHost(contents1
->GetSiteInstance()));
2220 iframe
->GetRenderFrameProxyHost(contents2
->GetSiteInstance()));
2223 } // namespace content