1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/command_line.h"
6 #include "base/files/file_path.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "base/test/histogram_tester.h"
9 #include "base/time/time.h"
10 #include "content/browser/compositor/test/no_transport_image_transport_factory.h"
11 #include "content/browser/frame_host/cross_site_transferring_request.h"
12 #include "content/browser/frame_host/navigation_controller_impl.h"
13 #include "content/browser/frame_host/navigation_entry_impl.h"
14 #include "content/browser/frame_host/navigation_request.h"
15 #include "content/browser/frame_host/navigator.h"
16 #include "content/browser/frame_host/render_frame_host_manager.h"
17 #include "content/browser/frame_host/render_frame_proxy_host.h"
18 #include "content/browser/site_instance_impl.h"
19 #include "content/browser/webui/web_ui_controller_factory_registry.h"
20 #include "content/common/frame_messages.h"
21 #include "content/common/site_isolation_policy.h"
22 #include "content/common/view_messages.h"
23 #include "content/public/browser/notification_details.h"
24 #include "content/public/browser/notification_service.h"
25 #include "content/public/browser/notification_source.h"
26 #include "content/public/browser/notification_types.h"
27 #include "content/public/browser/render_process_host.h"
28 #include "content/public/browser/render_widget_host_iterator.h"
29 #include "content/public/browser/web_contents_delegate.h"
30 #include "content/public/browser/web_contents_observer.h"
31 #include "content/public/browser/web_ui_controller.h"
32 #include "content/public/common/bindings_policy.h"
33 #include "content/public/common/content_switches.h"
34 #include "content/public/common/javascript_message_type.h"
35 #include "content/public/common/url_constants.h"
36 #include "content/public/common/url_utils.h"
37 #include "content/public/test/mock_render_process_host.h"
38 #include "content/public/test/test_notification_tracker.h"
39 #include "content/public/test/test_utils.h"
40 #include "content/test/test_content_browser_client.h"
41 #include "content/test/test_content_client.h"
42 #include "content/test/test_render_frame_host.h"
43 #include "content/test/test_render_view_host.h"
44 #include "content/test/test_web_contents.h"
45 #include "net/base/load_flags.h"
46 #include "testing/gtest/include/gtest/gtest.h"
47 #include "third_party/WebKit/public/web/WebSandboxFlags.h"
48 #include "ui/base/page_transition_types.h"
53 class RenderFrameHostManagerTestWebUIControllerFactory
54 : public WebUIControllerFactory
{
56 RenderFrameHostManagerTestWebUIControllerFactory()
57 : should_create_webui_(false) {
59 ~RenderFrameHostManagerTestWebUIControllerFactory() override
{}
61 void set_should_create_webui(bool should_create_webui
) {
62 should_create_webui_
= should_create_webui
;
65 // WebUIFactory implementation.
66 WebUIController
* CreateWebUIControllerForURL(WebUI
* web_ui
,
67 const GURL
& url
) const override
{
68 if (!(should_create_webui_
&& HasWebUIScheme(url
)))
70 return new WebUIController(web_ui
);
73 WebUI::TypeID
GetWebUIType(BrowserContext
* browser_context
,
74 const GURL
& url
) const override
{
75 return WebUI::kNoWebUI
;
78 bool UseWebUIForURL(BrowserContext
* browser_context
,
79 const GURL
& url
) const override
{
80 return HasWebUIScheme(url
);
83 bool UseWebUIBindingsForURL(BrowserContext
* browser_context
,
84 const GURL
& url
) const override
{
85 return HasWebUIScheme(url
);
89 bool should_create_webui_
;
91 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostManagerTestWebUIControllerFactory
);
94 class BeforeUnloadFiredWebContentsDelegate
: public WebContentsDelegate
{
96 BeforeUnloadFiredWebContentsDelegate() {}
97 ~BeforeUnloadFiredWebContentsDelegate() override
{}
99 void BeforeUnloadFired(WebContents
* web_contents
,
101 bool* proceed_to_fire_unload
) override
{
102 *proceed_to_fire_unload
= proceed
;
106 DISALLOW_COPY_AND_ASSIGN(BeforeUnloadFiredWebContentsDelegate
);
109 class CloseWebContentsDelegate
: public WebContentsDelegate
{
111 CloseWebContentsDelegate() : close_called_(false) {}
112 ~CloseWebContentsDelegate() override
{}
114 void CloseContents(WebContents
* web_contents
) override
{
115 close_called_
= true;
118 bool is_closed() { return close_called_
; }
121 DISALLOW_COPY_AND_ASSIGN(CloseWebContentsDelegate
);
126 // This observer keeps track of the last deleted RenderViewHost to avoid
127 // accessing it and causing use-after-free condition.
128 class RenderViewHostDeletedObserver
: public WebContentsObserver
{
130 RenderViewHostDeletedObserver(RenderViewHost
* rvh
)
131 : WebContentsObserver(WebContents::FromRenderViewHost(rvh
)),
132 process_id_(rvh
->GetProcess()->GetID()),
133 routing_id_(rvh
->GetRoutingID()),
137 void RenderViewDeleted(RenderViewHost
* render_view_host
) override
{
138 if (render_view_host
->GetProcess()->GetID() == process_id_
&&
139 render_view_host
->GetRoutingID() == routing_id_
) {
153 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDeletedObserver
);
156 // This observer keeps track of the last created RenderFrameHost to allow tests
157 // to ensure that no RenderFrameHost objects are created when not expected.
158 class RenderFrameHostCreatedObserver
: public WebContentsObserver
{
160 RenderFrameHostCreatedObserver(WebContents
* web_contents
)
161 : WebContentsObserver(web_contents
),
165 void RenderFrameCreated(RenderFrameHost
* render_frame_host
) override
{
176 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostCreatedObserver
);
179 // This WebContents observer keep track of its RVH change.
180 class RenderViewHostChangedObserver
: public WebContentsObserver
{
182 RenderViewHostChangedObserver(WebContents
* web_contents
)
183 : WebContentsObserver(web_contents
), host_changed_(false) {}
185 // WebContentsObserver.
186 void RenderViewHostChanged(RenderViewHost
* old_host
,
187 RenderViewHost
* new_host
) override
{
188 host_changed_
= true;
191 bool DidHostChange() {
192 bool host_changed
= host_changed_
;
197 void Reset() { host_changed_
= false; }
201 DISALLOW_COPY_AND_ASSIGN(RenderViewHostChangedObserver
);
204 // This observer is used to check whether IPC messages are being filtered for
205 // swapped out RenderFrameHost objects. It observes the plugin crash and favicon
206 // update events, which the FilterMessagesWhileSwappedOut test simulates being
207 // sent. The test is successful if the event is not observed.
208 // See http://crbug.com/351815
209 class PluginFaviconMessageObserver
: public WebContentsObserver
{
211 PluginFaviconMessageObserver(WebContents
* web_contents
)
212 : WebContentsObserver(web_contents
),
213 plugin_crashed_(false),
214 favicon_received_(false) { }
216 void PluginCrashed(const base::FilePath
& plugin_path
,
217 base::ProcessId plugin_pid
) override
{
218 plugin_crashed_
= true;
221 void DidUpdateFaviconURL(const std::vector
<FaviconURL
>& candidates
) override
{
222 favicon_received_
= true;
225 bool plugin_crashed() {
226 return plugin_crashed_
;
229 bool favicon_received() {
230 return favicon_received_
;
234 bool plugin_crashed_
;
235 bool favicon_received_
;
237 DISALLOW_COPY_AND_ASSIGN(PluginFaviconMessageObserver
);
242 class RenderFrameHostManagerTest
: public RenderViewHostImplTestHarness
{
244 void SetUp() override
{
245 RenderViewHostImplTestHarness::SetUp();
246 WebUIControllerFactory::RegisterFactory(&factory_
);
247 #if !defined(OS_ANDROID)
248 ImageTransportFactory::InitializeForUnitTests(
249 make_scoped_ptr(new NoTransportImageTransportFactory
));
253 void TearDown() override
{
254 RenderViewHostImplTestHarness::TearDown();
255 WebUIControllerFactory::UnregisterFactoryForTesting(&factory_
);
256 #if !defined(OS_ANDROID)
257 // RenderWidgetHostView holds on to a reference to SurfaceManager, so it
258 // must be shut down before the ImageTransportFactory.
259 ImageTransportFactory::Terminate();
263 void set_should_create_webui(bool should_create_webui
) {
264 factory_
.set_should_create_webui(should_create_webui
);
267 void NavigateActiveAndCommit(const GURL
& url
) {
268 // Note: we navigate the active RenderFrameHost because previous navigations
269 // won't have committed yet, so NavigateAndCommit does the wrong thing
271 controller().LoadURL(
272 url
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
273 int entry_id
= controller().GetPendingEntry()->GetUniqueID();
275 // Simulate the BeforeUnload_ACK that is received from the current renderer
276 // for a cross-site navigation.
277 // PlzNavigate: it is necessary to call PrepareForCommit before getting the
278 // main and the pending frame because when we are trying to navigate to a
279 // WebUI from a new tab, a RenderFrameHost is created to display it that is
280 // committed immediately (since it is a new tab). Therefore the main frame
281 // is replaced without a pending frame being created, and we don't get the
282 // right values for the RFH to navigate: we try to use the old one that has
283 // been deleted in the meantime.
284 contents()->GetMainFrame()->PrepareForCommit();
286 TestRenderFrameHost
* old_rfh
= contents()->GetMainFrame();
287 TestRenderFrameHost
* active_rfh
= contents()->GetPendingMainFrame()
288 ? contents()->GetPendingMainFrame()
290 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, old_rfh
->rfh_state());
292 // Commit the navigation with a new page ID.
293 int32 max_page_id
= contents()->GetMaxPageIDForSiteInstance(
294 active_rfh
->GetSiteInstance());
296 // Use an observer to avoid accessing a deleted renderer later on when the
297 // state is being checked.
298 RenderFrameDeletedObserver
rfh_observer(old_rfh
);
299 RenderViewHostDeletedObserver
rvh_observer(old_rfh
->GetRenderViewHost());
300 active_rfh
->SendNavigate(max_page_id
+ 1, entry_id
, true, url
);
302 // Make sure that we start to run the unload handler at the time of commit.
303 bool expecting_rfh_shutdown
= false;
304 if (old_rfh
!= active_rfh
&& !rfh_observer
.deleted()) {
305 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
,
306 old_rfh
->rfh_state());
307 if (!old_rfh
->GetSiteInstance()->active_frame_count() ||
308 SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
309 expecting_rfh_shutdown
= true;
311 old_rfh
->frame_tree_node()->render_manager()->IsPendingDeletion(
316 // Simulate the swap out ACK coming from the pending renderer. This should
317 // either shut down the old RFH or leave it in a swapped out state.
318 if (old_rfh
!= active_rfh
) {
319 old_rfh
->OnSwappedOut();
320 if (expecting_rfh_shutdown
) {
321 EXPECT_TRUE(rfh_observer
.deleted());
322 if (!SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
323 EXPECT_TRUE(rvh_observer
.deleted());
326 EXPECT_EQ(RenderFrameHostImpl::STATE_SWAPPED_OUT
,
327 old_rfh
->rfh_state());
330 EXPECT_EQ(active_rfh
, contents()->GetMainFrame());
331 EXPECT_EQ(NULL
, contents()->GetPendingMainFrame());
334 bool ShouldSwapProcesses(RenderFrameHostManager
* manager
,
335 const NavigationEntryImpl
* current_entry
,
336 const NavigationEntryImpl
* new_entry
) const {
338 BrowserContext
* browser_context
=
339 manager
->delegate_
->GetControllerForRenderManager().GetBrowserContext();
340 const GURL
& current_effective_url
= current_entry
?
341 SiteInstanceImpl::GetEffectiveURL(browser_context
,
342 current_entry
->GetURL()) :
343 manager
->render_frame_host_
->GetSiteInstance()->GetSiteURL();
344 bool current_is_view_source_mode
= current_entry
?
345 current_entry
->IsViewSourceMode() : new_entry
->IsViewSourceMode();
346 return manager
->ShouldSwapBrowsingInstancesForNavigation(
347 current_effective_url
,
348 current_is_view_source_mode
,
349 new_entry
->site_instance(),
350 SiteInstanceImpl::GetEffectiveURL(browser_context
, new_entry
->GetURL()),
351 new_entry
->IsViewSourceMode());
354 // Creates a test RenderFrameHost that's swapped out.
355 TestRenderFrameHost
* CreateSwappedOutRenderFrameHost() {
356 const GURL
kChromeURL("chrome://foo");
357 const GURL
kDestUrl("http://www.google.com/");
359 // Navigate our first tab to a chrome url and then to the destination.
360 NavigateActiveAndCommit(kChromeURL
);
361 TestRenderFrameHost
* ntp_rfh
= contents()->GetMainFrame();
363 // Navigate to a cross-site URL.
364 contents()->GetController().LoadURL(
365 kDestUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
366 int entry_id
= contents()->GetController().GetPendingEntry()->GetUniqueID();
367 contents()->GetMainFrame()->PrepareForCommit();
368 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
370 // Manually increase the number of active frames in the
371 // SiteInstance that ntp_rfh belongs to, to prevent it from being
372 // destroyed when it gets swapped out.
373 ntp_rfh
->GetSiteInstance()->increment_active_frame_count();
375 TestRenderFrameHost
* dest_rfh
= contents()->GetPendingMainFrame();
377 EXPECT_NE(ntp_rfh
, dest_rfh
);
379 // BeforeUnload finishes.
380 ntp_rfh
->SendBeforeUnloadACK(true);
382 dest_rfh
->SendNavigate(101, entry_id
, true, kDestUrl
);
383 ntp_rfh
->OnSwappedOut();
385 EXPECT_TRUE(ntp_rfh
->is_swapped_out());
389 // Returns the RenderFrameHost that should be used in the navigation to
391 RenderFrameHostImpl
* NavigateToEntry(
392 RenderFrameHostManager
* manager
,
393 const NavigationEntryImpl
& entry
) {
394 // Tests currently only navigate using main frame FrameNavigationEntries.
395 FrameNavigationEntry
* frame_entry
= entry
.root_node()->frame_entry
.get();
396 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
397 switches::kEnableBrowserSideNavigation
)) {
398 scoped_ptr
<NavigationRequest
> navigation_request
=
399 NavigationRequest::CreateBrowserInitiated(
400 manager
->frame_tree_node_
, frame_entry
->url(),
401 frame_entry
->referrer(), *frame_entry
, entry
,
402 FrameMsg_Navigate_Type::NORMAL
, false, base::TimeTicks::Now(),
403 static_cast<NavigationControllerImpl
*>(&controller()));
404 TestRenderFrameHost
* frame_host
= static_cast<TestRenderFrameHost
*>(
405 manager
->GetFrameHostForNavigation(*navigation_request
));
407 frame_host
->set_pending_commit(true);
411 return manager
->Navigate(frame_entry
->url(), *frame_entry
, entry
);
414 // Returns the pending RenderFrameHost.
415 // PlzNavigate: returns the speculative RenderFrameHost.
416 RenderFrameHostImpl
* GetPendingFrameHost(
417 RenderFrameHostManager
* manager
) {
418 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
419 switches::kEnableBrowserSideNavigation
)) {
420 return manager
->speculative_render_frame_host_
.get();
422 return manager
->pending_frame_host();
425 // Exposes RenderFrameHostManager::CollectOpenerFrameTrees for testing.
426 void CollectOpenerFrameTrees(
428 std::vector
<FrameTree
*>* opener_frame_trees
,
429 base::hash_set
<FrameTreeNode
*>* nodes_with_back_links
) {
430 node
->render_manager()->CollectOpenerFrameTrees(opener_frame_trees
,
431 nodes_with_back_links
);
435 RenderFrameHostManagerTestWebUIControllerFactory factory_
;
438 // Tests that when you navigate from a chrome:// url to another page, and
439 // then do that same thing in another tab, that the two resulting pages have
440 // different SiteInstances, BrowsingInstances, and RenderProcessHosts. This is
441 // a regression test for bug 9364.
442 TEST_F(RenderFrameHostManagerTest
, NewTabPageProcesses
) {
443 set_should_create_webui(true);
444 const GURL
kChromeUrl("chrome://foo");
445 const GURL
kDestUrl("http://www.google.com/");
447 // Navigate our first tab to the chrome url and then to the destination,
448 // ensuring we grant bindings to the chrome URL.
449 NavigateActiveAndCommit(kChromeUrl
);
450 EXPECT_TRUE(active_rvh()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
451 NavigateActiveAndCommit(kDestUrl
);
453 EXPECT_FALSE(contents()->GetPendingMainFrame());
455 // Make a second tab.
456 scoped_ptr
<TestWebContents
> contents2(
457 TestWebContents::Create(browser_context(), NULL
));
459 // Load the two URLs in the second tab. Note that the first navigation creates
460 // a RFH that's not pending (since there is no cross-site transition), so
461 // we use the committed one.
462 contents2
->GetController().LoadURL(
463 kChromeUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
464 int entry_id
= contents2
->GetController().GetPendingEntry()->GetUniqueID();
465 contents2
->GetMainFrame()->PrepareForCommit();
466 TestRenderFrameHost
* ntp_rfh2
= contents2
->GetMainFrame();
467 EXPECT_FALSE(contents2
->CrossProcessNavigationPending());
468 ntp_rfh2
->SendNavigate(100, entry_id
, true, kChromeUrl
);
470 // The second one is the opposite, creating a cross-site transition and
471 // requiring a beforeunload ack.
472 contents2
->GetController().LoadURL(
473 kDestUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
474 entry_id
= contents2
->GetController().GetPendingEntry()->GetUniqueID();
475 contents2
->GetMainFrame()->PrepareForCommit();
476 EXPECT_TRUE(contents2
->CrossProcessNavigationPending());
477 TestRenderFrameHost
* dest_rfh2
= contents2
->GetPendingMainFrame();
478 ASSERT_TRUE(dest_rfh2
);
480 dest_rfh2
->SendNavigate(101, entry_id
, true, kDestUrl
);
482 // The two RFH's should be different in every way.
483 EXPECT_NE(contents()->GetMainFrame()->GetProcess(), dest_rfh2
->GetProcess());
484 EXPECT_NE(contents()->GetMainFrame()->GetSiteInstance(),
485 dest_rfh2
->GetSiteInstance());
486 EXPECT_FALSE(dest_rfh2
->GetSiteInstance()->IsRelatedSiteInstance(
487 contents()->GetMainFrame()->GetSiteInstance()));
489 // Navigate both to the new tab page, and verify that they share a
490 // RenderProcessHost (not a SiteInstance).
491 NavigateActiveAndCommit(kChromeUrl
);
492 EXPECT_FALSE(contents()->GetPendingMainFrame());
494 contents2
->GetController().LoadURL(
495 kChromeUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
496 entry_id
= contents2
->GetController().GetPendingEntry()->GetUniqueID();
497 contents2
->GetMainFrame()->PrepareForCommit();
498 contents2
->GetPendingMainFrame()->SendNavigate(102, entry_id
, true,
501 EXPECT_NE(contents()->GetMainFrame()->GetSiteInstance(),
502 contents2
->GetMainFrame()->GetSiteInstance());
503 EXPECT_EQ(contents()->GetMainFrame()->GetSiteInstance()->GetProcess(),
504 contents2
->GetMainFrame()->GetSiteInstance()->GetProcess());
507 // Ensure that the browser ignores most IPC messages that arrive from a
508 // RenderViewHost that has been swapped out. We do not want to take
509 // action on requests from a non-active renderer. The main exception is
510 // for synchronous messages, which cannot be ignored without leaving the
511 // renderer in a stuck state. See http://crbug.com/93427.
512 TEST_F(RenderFrameHostManagerTest
, FilterMessagesWhileSwappedOut
) {
513 const GURL
kChromeURL("chrome://foo");
514 const GURL
kDestUrl("http://www.google.com/");
515 std::vector
<FaviconURL
> icons
;
517 // Navigate our first tab to a chrome url and then to the destination.
518 NavigateActiveAndCommit(kChromeURL
);
519 TestRenderFrameHost
* ntp_rfh
= contents()->GetMainFrame();
520 TestRenderViewHost
* ntp_rvh
= ntp_rfh
->GetRenderViewHost();
522 // Send an update favicon message and make sure it works.
524 PluginFaviconMessageObserver
observer(contents());
525 EXPECT_TRUE(ntp_rfh
->GetRenderViewHost()->OnMessageReceived(
526 ViewHostMsg_UpdateFaviconURL(
527 ntp_rfh
->GetRenderViewHost()->GetRoutingID(), icons
)));
528 EXPECT_TRUE(observer
.favicon_received());
530 // Create one more frame in the same SiteInstance where ntp_rfh
531 // exists so that it doesn't get deleted on navigation to another
533 ntp_rfh
->GetSiteInstance()->increment_active_frame_count();
535 // Navigate to a cross-site URL.
536 NavigateActiveAndCommit(kDestUrl
);
537 TestRenderFrameHost
* dest_rfh
= contents()->GetMainFrame();
538 ASSERT_TRUE(dest_rfh
);
539 EXPECT_NE(ntp_rfh
, dest_rfh
);
541 // The new RVH should be able to update its favicon.
543 PluginFaviconMessageObserver
observer(contents());
545 dest_rfh
->GetRenderViewHost()->OnMessageReceived(
546 ViewHostMsg_UpdateFaviconURL(
547 dest_rfh
->GetRenderViewHost()->GetRoutingID(), icons
)));
548 EXPECT_TRUE(observer
.favicon_received());
551 // The old renderer, being slow, now updates the favicon. It should be
552 // filtered out and not take effect.
554 PluginFaviconMessageObserver
observer(contents());
556 ntp_rvh
->OnMessageReceived(
557 ViewHostMsg_UpdateFaviconURL(
558 dest_rfh
->GetRenderViewHost()->GetRoutingID(), icons
)));
559 EXPECT_FALSE(observer
.favicon_received());
562 // In --site-per-process, the RenderFrameHost is deleted on cross-process
563 // navigation, so the rest of the test case doesn't apply.
564 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
568 #if defined(ENABLE_PLUGINS)
569 // The same logic should apply to RenderFrameHosts as well and routing through
570 // swapped out RFH shouldn't be allowed. Use a PluginCrashObserver to check
571 // if the IPC message is allowed through or not.
573 PluginFaviconMessageObserver
observer(contents());
574 EXPECT_TRUE(ntp_rfh
->OnMessageReceived(
575 FrameHostMsg_PluginCrashed(
576 ntp_rfh
->GetRoutingID(), base::FilePath(), 0)));
577 EXPECT_FALSE(observer
.plugin_crashed());
581 // We cannot filter out synchronous IPC messages, because the renderer would
582 // be left waiting for a reply. We pick RunBeforeUnloadConfirm as an example
583 // that can run easily within a unit test, and that needs to receive a reply
584 // without showing an actual dialog.
585 MockRenderProcessHost
* ntp_process_host
= ntp_rfh
->GetProcess();
586 ntp_process_host
->sink().ClearMessages();
587 const base::string16 msg
= base::ASCIIToUTF16("Message");
589 base::string16 unused
;
590 FrameHostMsg_RunBeforeUnloadConfirm
before_unload_msg(
591 ntp_rfh
->GetRoutingID(), kChromeURL
, msg
, false, &result
, &unused
);
592 // Enable pumping for check in BrowserMessageFilter::CheckCanDispatchOnUI.
593 before_unload_msg
.EnableMessagePumping();
594 EXPECT_TRUE(ntp_rfh
->OnMessageReceived(before_unload_msg
));
595 EXPECT_TRUE(ntp_process_host
->sink().GetUniqueMessageMatching(IPC_REPLY_ID
));
597 // Also test RunJavaScriptMessage.
598 ntp_process_host
->sink().ClearMessages();
599 FrameHostMsg_RunJavaScriptMessage
js_msg(
600 ntp_rfh
->GetRoutingID(), msg
, msg
, kChromeURL
,
601 JAVASCRIPT_MESSAGE_TYPE_CONFIRM
, &result
, &unused
);
602 js_msg
.EnableMessagePumping();
603 EXPECT_TRUE(ntp_rfh
->OnMessageReceived(js_msg
));
604 EXPECT_TRUE(ntp_process_host
->sink().GetUniqueMessageMatching(IPC_REPLY_ID
));
607 // Test that the ViewHostMsg_UpdateFaviconURL IPC message is ignored if the
608 // renderer is in the STATE_PENDING_SWAP_OUT_STATE. The favicon code assumes
609 // that it only gets ViewHostMsg_UpdateFaviconURL messages for the most recently
610 // committed navigation for each WebContentsImpl.
611 TEST_F(RenderFrameHostManagerTest
, UpdateFaviconURLWhilePendingSwapOut
) {
612 const GURL
kChromeURL("chrome://foo");
613 const GURL
kDestUrl("http://www.google.com/");
614 std::vector
<FaviconURL
> icons
;
616 // Navigate our first tab to a chrome url and then to the destination.
617 NavigateActiveAndCommit(kChromeURL
);
618 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
620 // Send an update favicon message and make sure it works.
622 PluginFaviconMessageObserver
observer(contents());
623 EXPECT_TRUE(rfh1
->GetRenderViewHost()->OnMessageReceived(
624 ViewHostMsg_UpdateFaviconURL(
625 rfh1
->GetRenderViewHost()->GetRoutingID(), icons
)));
626 EXPECT_TRUE(observer
.favicon_received());
629 // Create one more frame in the same SiteInstance where |rfh1| exists so that
630 // it doesn't get deleted on navigation to another site.
631 rfh1
->GetSiteInstance()->increment_active_frame_count();
633 // Navigate to a cross-site URL and commit the new page.
634 controller().LoadURL(
635 kDestUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
636 int entry_id
= controller().GetPendingEntry()->GetUniqueID();
637 contents()->GetMainFrame()->PrepareForCommit();
638 TestRenderFrameHost
* rfh2
= contents()->GetPendingMainFrame();
639 contents()->TestDidNavigate(rfh2
, 1, entry_id
, true, kDestUrl
,
640 ui::PAGE_TRANSITION_TYPED
);
641 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
642 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
644 // The new RVH should be able to update its favicons.
646 PluginFaviconMessageObserver
observer(contents());
647 EXPECT_TRUE(rfh2
->GetRenderViewHost()->OnMessageReceived(
648 ViewHostMsg_UpdateFaviconURL(rfh2
->GetRenderViewHost()->GetRoutingID(),
650 EXPECT_TRUE(observer
.favicon_received());
653 // The old renderer, being slow, now updates its favicons. The message should
656 PluginFaviconMessageObserver
observer(contents());
657 EXPECT_TRUE(rfh1
->GetRenderViewHost()->OnMessageReceived(
658 ViewHostMsg_UpdateFaviconURL(rfh1
->GetRenderViewHost()->GetRoutingID(),
660 EXPECT_FALSE(observer
.favicon_received());
664 // Ensure that frames aren't added to the frame tree, if the message is coming
665 // from a process different than the parent frame's current RenderFrameHost
666 // process. Otherwise it is possible to have collisions of routing ids, as they
667 // are scoped per process. See https://crbug.com/415059.
668 TEST_F(RenderFrameHostManagerTest
, DropCreateChildFrameWhileSwappedOut
) {
669 const GURL
kUrl1("http://foo.com");
670 const GURL
kUrl2("http://www.google.com/");
672 // This test is invalid in --site-per-process mode, as swapped-out is no
674 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
678 // Navigate to the first site.
679 NavigateActiveAndCommit(kUrl1
);
680 TestRenderFrameHost
* initial_rfh
= contents()->GetMainFrame();
682 RenderFrameHostCreatedObserver
observer(contents());
683 initial_rfh
->OnCreateChildFrame(
684 initial_rfh
->GetProcess()->GetNextRoutingID(),
685 blink::WebTreeScopeType::Document
, std::string(),
686 blink::WebSandboxFlags::None
);
687 EXPECT_TRUE(observer
.created());
690 // Create one more frame in the same SiteInstance where initial_rfh
691 // exists so that initial_rfh doesn't get deleted on navigation to another
693 initial_rfh
->GetSiteInstance()->increment_active_frame_count();
695 // Navigate to a cross-site URL.
696 NavigateActiveAndCommit(kUrl2
);
697 EXPECT_TRUE(initial_rfh
->is_swapped_out());
699 TestRenderFrameHost
* dest_rfh
= contents()->GetMainFrame();
700 ASSERT_TRUE(dest_rfh
);
701 EXPECT_NE(initial_rfh
, dest_rfh
);
704 // Since the old RFH is now swapped out, it shouldn't process any messages
705 // to create child frames.
706 RenderFrameHostCreatedObserver
observer(contents());
707 initial_rfh
->OnCreateChildFrame(
708 initial_rfh
->GetProcess()->GetNextRoutingID(),
709 blink::WebTreeScopeType::Document
, std::string(),
710 blink::WebSandboxFlags::None
);
711 EXPECT_FALSE(observer
.created());
715 TEST_F(RenderFrameHostManagerTest
, WhiteListSwapCompositorFrame
) {
716 // TODO(nasko): Check with kenrb whether this test can be rewritten and
717 // whether it makes sense when swapped out is replaced with proxies.
718 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
721 TestRenderFrameHost
* swapped_out_rfh
= CreateSwappedOutRenderFrameHost();
722 TestRenderWidgetHostView
* swapped_out_rwhv
=
723 static_cast<TestRenderWidgetHostView
*>(
724 swapped_out_rfh
->GetRenderViewHost()->GetView());
725 EXPECT_FALSE(swapped_out_rwhv
->did_swap_compositor_frame());
727 MockRenderProcessHost
* process_host
= swapped_out_rfh
->GetProcess();
728 process_host
->sink().ClearMessages();
730 cc::CompositorFrame frame
;
731 ViewHostMsg_SwapCompositorFrame
msg(
732 rvh()->GetRoutingID(), 0, frame
, std::vector
<IPC::Message
>());
734 EXPECT_TRUE(swapped_out_rfh
->render_view_host()->OnMessageReceived(msg
));
735 EXPECT_TRUE(swapped_out_rwhv
->did_swap_compositor_frame());
738 // Test if RenderViewHost::GetRenderWidgetHosts() only returns active
740 TEST_F(RenderFrameHostManagerTest
, GetRenderWidgetHostsReturnsActiveViews
) {
741 // This test is invalid in --site-per-process mode, as swapped-out is no
743 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
747 TestRenderFrameHost
* swapped_out_rfh
= CreateSwappedOutRenderFrameHost();
748 EXPECT_TRUE(swapped_out_rfh
->is_swapped_out());
750 scoped_ptr
<RenderWidgetHostIterator
> widgets(
751 RenderWidgetHost::GetRenderWidgetHosts());
752 // We know that there is the only one active widget. Another view is
753 // now swapped out, so the swapped out view is not included in the
755 RenderWidgetHost
* widget
= widgets
->GetNextHost();
756 EXPECT_FALSE(widgets
->GetNextHost());
757 RenderViewHost
* rvh
= RenderViewHost::From(widget
);
758 EXPECT_TRUE(static_cast<RenderViewHostImpl
*>(rvh
)->is_active());
761 // Test if RenderViewHost::GetRenderWidgetHosts() returns a subset of
762 // RenderViewHostImpl::GetAllRenderWidgetHosts().
763 // RenderViewHost::GetRenderWidgetHosts() returns only active widgets, but
764 // RenderViewHostImpl::GetAllRenderWidgetHosts() returns everything
765 // including swapped out ones.
766 TEST_F(RenderFrameHostManagerTest
,
767 GetRenderWidgetHostsWithinGetAllRenderWidgetHosts
) {
768 // This test is invalid in --site-per-process mode, as swapped-out is no
770 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
774 TestRenderFrameHost
* swapped_out_rfh
= CreateSwappedOutRenderFrameHost();
775 EXPECT_TRUE(swapped_out_rfh
->is_swapped_out());
777 scoped_ptr
<RenderWidgetHostIterator
> widgets(
778 RenderWidgetHost::GetRenderWidgetHosts());
780 while (RenderWidgetHost
* w
= widgets
->GetNextHost()) {
782 scoped_ptr
<RenderWidgetHostIterator
> all_widgets(
783 RenderWidgetHostImpl::GetAllRenderWidgetHosts());
784 while (RenderWidgetHost
* widget
= all_widgets
->GetNextHost()) {
794 // Test if SiteInstanceImpl::active_frame_count() is correctly updated
795 // as frames in a SiteInstance get swapped out and in.
796 TEST_F(RenderFrameHostManagerTest
, ActiveFrameCountWhileSwappingInAndOut
) {
797 const GURL
kUrl1("http://www.google.com/");
798 const GURL
kUrl2("http://www.chromium.org/");
800 // Navigate to an initial URL.
801 contents()->NavigateAndCommit(kUrl1
);
802 TestRenderFrameHost
* rfh1
= main_test_rfh();
804 SiteInstanceImpl
* instance1
= rfh1
->GetSiteInstance();
805 EXPECT_EQ(instance1
->active_frame_count(), 1U);
807 // Create 2 new tabs and simulate them being the opener chain for the main
808 // tab. They should be in the same SiteInstance.
809 scoped_ptr
<TestWebContents
> opener1(
810 TestWebContents::Create(browser_context(), instance1
));
811 contents()->SetOpener(opener1
.get());
813 scoped_ptr
<TestWebContents
> opener2(
814 TestWebContents::Create(browser_context(), instance1
));
815 opener1
->SetOpener(opener2
.get());
817 EXPECT_EQ(instance1
->active_frame_count(), 3U);
819 // Navigate to a cross-site URL (different SiteInstance but same
820 // BrowsingInstance).
821 contents()->NavigateAndCommit(kUrl2
);
822 TestRenderFrameHost
* rfh2
= main_test_rfh();
823 SiteInstanceImpl
* instance2
= rfh2
->GetSiteInstance();
825 // rvh2 is on chromium.org which is different from google.com on
826 // which other tabs are.
827 EXPECT_EQ(instance2
->active_frame_count(), 1U);
829 // There are two active views on google.com now.
830 EXPECT_EQ(instance1
->active_frame_count(), 2U);
832 // Navigate to the original origin (google.com).
833 contents()->NavigateAndCommit(kUrl1
);
835 EXPECT_EQ(instance1
->active_frame_count(), 3U);
838 // This deletes a WebContents when the given RVH is deleted. This is
839 // only for testing whether deleting an RVH does not cause any UaF in
840 // other parts of the system. For now, this class is only used for the
841 // next test cases to detect the bug mentioned at
842 // http://crbug.com/259859.
843 class RenderViewHostDestroyer
: public WebContentsObserver
{
845 RenderViewHostDestroyer(RenderViewHost
* render_view_host
,
846 WebContents
* web_contents
)
847 : WebContentsObserver(WebContents::FromRenderViewHost(render_view_host
)),
848 render_view_host_(render_view_host
),
849 web_contents_(web_contents
) {}
851 void RenderViewDeleted(RenderViewHost
* render_view_host
) override
{
852 if (render_view_host
== render_view_host_
)
853 delete web_contents_
;
857 RenderViewHost
* render_view_host_
;
858 WebContents
* web_contents_
;
860 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestroyer
);
863 // Test if ShutdownRenderViewHostsInSiteInstance() does not touch any
864 // RenderWidget that has been freed while deleting a RenderViewHost in
865 // a previous iteration. This is a regression test for
866 // http://crbug.com/259859.
867 TEST_F(RenderFrameHostManagerTest
,
868 DetectUseAfterFreeInShutdownRenderViewHostsInSiteInstance
) {
869 const GURL
kChromeURL("chrome://newtab");
870 const GURL
kUrl1("http://www.google.com");
871 const GURL
kUrl2("http://www.chromium.org");
873 // Navigate our first tab to a chrome url and then to the destination.
874 NavigateActiveAndCommit(kChromeURL
);
875 TestRenderFrameHost
* ntp_rfh
= contents()->GetMainFrame();
877 // Create one more tab and navigate to kUrl1. web_contents is not
878 // wrapped as scoped_ptr since it intentionally deleted by destroyer
879 // below as part of this test.
880 TestWebContents
* web_contents
=
881 TestWebContents::Create(browser_context(), ntp_rfh
->GetSiteInstance());
882 web_contents
->NavigateAndCommit(kUrl1
);
883 RenderViewHostDestroyer
destroyer(ntp_rfh
->GetRenderViewHost(),
886 // This causes the first tab to navigate to kUrl2, which destroys
887 // the ntp_rfh in ShutdownRenderViewHostsInSiteInstance(). When
888 // ntp_rfh is destroyed, it also destroys the RVHs in web_contents
889 // too. This can test whether
890 // SiteInstanceImpl::ShutdownRenderViewHostsInSiteInstance() can
891 // touch any object freed in this way or not while iterating through
893 contents()->NavigateAndCommit(kUrl2
);
896 // When there is an error with the specified page, renderer exits view-source
897 // mode. See WebFrameImpl::DidFail(). We check by this test that
898 // EnableViewSourceMode message is sent on every navigation regardless
899 // RenderView is being newly created or reused.
900 TEST_F(RenderFrameHostManagerTest
, AlwaysSendEnableViewSourceMode
) {
901 const GURL
kChromeUrl("chrome://foo/");
902 const GURL
kUrl("http://foo/");
903 const GURL
kViewSourceUrl("view-source:http://foo/");
905 // We have to navigate to some page at first since without this, the first
906 // navigation will reuse the SiteInstance created by Init(), and the second
907 // one will create a new SiteInstance. Because current_instance and
908 // new_instance will be different, a new RenderViewHost will be created for
909 // the second navigation. We have to avoid this in order to exercise the
911 NavigateActiveAndCommit(kChromeUrl
);
913 // Navigate. Note that "view source" URLs are implemented by putting the RFH
914 // into a view-source mode and then navigating to the inner URL, so that's why
915 // the bare URL is what's committed and returned by the last committed entry's
917 controller().LoadURL(
918 kViewSourceUrl
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
919 int entry_id
= controller().GetPendingEntry()->GetUniqueID();
921 // Simulate response from RenderFrame for DispatchBeforeUnload.
922 contents()->GetMainFrame()->PrepareForCommit();
923 ASSERT_TRUE(contents()->GetPendingMainFrame())
924 << "Expected new pending RenderFrameHost to be created.";
925 RenderFrameHost
* last_rfh
= contents()->GetPendingMainFrame();
927 contents()->GetMaxPageIDForSiteInstance(last_rfh
->GetSiteInstance()) + 1;
928 contents()->GetPendingMainFrame()->SendNavigate(new_id
, entry_id
, true, kUrl
);
930 EXPECT_EQ(1, controller().GetLastCommittedEntryIndex());
931 NavigationEntry
* last_committed
= controller().GetLastCommittedEntry();
932 ASSERT_NE(nullptr, last_committed
);
933 EXPECT_EQ(kUrl
, last_committed
->GetURL());
934 EXPECT_EQ(kViewSourceUrl
, last_committed
->GetVirtualURL());
935 EXPECT_FALSE(controller().GetPendingEntry());
936 // Because we're using TestWebContents and TestRenderViewHost in this
937 // unittest, no one calls WebContentsImpl::RenderViewCreated(). So, we see no
938 // EnableViewSourceMode message, here.
940 // Clear queued messages before load.
941 process()->sink().ClearMessages();
944 controller().LoadURL(
945 kViewSourceUrl
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
946 entry_id
= controller().GetPendingEntry()->GetUniqueID();
947 contents()->GetMainFrame()->PrepareForCommit();
949 // The same RenderViewHost should be reused.
950 EXPECT_FALSE(contents()->GetPendingMainFrame());
951 EXPECT_EQ(last_rfh
, contents()->GetMainFrame());
953 // The renderer sends a commit.
954 contents()->GetMainFrame()->SendNavigateWithTransition(
955 new_id
, entry_id
, false, kUrl
, ui::PAGE_TRANSITION_TYPED
);
956 EXPECT_EQ(1, controller().GetLastCommittedEntryIndex());
957 EXPECT_FALSE(controller().GetPendingEntry());
959 // New message should be sent out to make sure to enter view-source mode.
960 EXPECT_TRUE(process()->sink().GetUniqueMessageMatching(
961 ViewMsg_EnableViewSourceMode::ID
));
964 // Tests the Init function by checking the initial RenderViewHost.
965 TEST_F(RenderFrameHostManagerTest
, Init
) {
966 // Using TestBrowserContext.
967 SiteInstanceImpl
* instance
=
968 static_cast<SiteInstanceImpl
*>(SiteInstance::Create(browser_context()));
969 EXPECT_FALSE(instance
->HasSite());
971 scoped_ptr
<TestWebContents
> web_contents(
972 TestWebContents::Create(browser_context(), instance
));
974 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
975 RenderViewHostImpl
* rvh
= manager
->current_host();
976 RenderFrameHostImpl
* rfh
= manager
->current_frame_host();
979 EXPECT_EQ(rvh
, rfh
->render_view_host());
980 EXPECT_EQ(instance
, rvh
->GetSiteInstance());
981 EXPECT_EQ(web_contents
.get(), rvh
->GetDelegate());
982 EXPECT_EQ(web_contents
.get(), rfh
->delegate());
983 EXPECT_TRUE(manager
->GetRenderWidgetHostView());
984 EXPECT_FALSE(manager
->pending_render_view_host());
987 // Tests the Navigate function. We navigate three sites consecutively and check
988 // how the pending/committed RenderViewHost are modified.
989 TEST_F(RenderFrameHostManagerTest
, Navigate
) {
990 SiteInstance
* instance
= SiteInstance::Create(browser_context());
992 scoped_ptr
<TestWebContents
> web_contents(
993 TestWebContents::Create(browser_context(), instance
));
994 RenderViewHostChangedObserver
change_observer(web_contents
.get());
996 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
997 RenderFrameHostImpl
* host
= NULL
;
999 // 1) The first navigation. --------------------------
1000 const GURL
kUrl1("http://www.google.com/");
1001 NavigationEntryImpl
entry1(
1002 NULL
/* instance */, -1 /* page_id */, kUrl1
, Referrer(),
1003 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED
,
1004 false /* is_renderer_init */);
1005 host
= NavigateToEntry(manager
, entry1
);
1007 // The RenderFrameHost created in Init will be reused.
1008 EXPECT_TRUE(host
== manager
->current_frame_host());
1009 EXPECT_FALSE(GetPendingFrameHost(manager
));
1012 manager
->DidNavigateFrame(host
, true);
1013 // Commit to SiteInstance should be delayed until RenderFrame commit.
1014 EXPECT_TRUE(host
== manager
->current_frame_host());
1016 EXPECT_FALSE(host
->GetSiteInstance()->HasSite());
1017 host
->GetSiteInstance()->SetSite(kUrl1
);
1019 // 2) Navigate to next site. -------------------------
1020 const GURL
kUrl2("http://www.google.com/foo");
1021 NavigationEntryImpl
entry2(
1022 NULL
/* instance */, -1 /* page_id */, kUrl2
,
1023 Referrer(kUrl1
, blink::WebReferrerPolicyDefault
),
1024 base::string16() /* title */, ui::PAGE_TRANSITION_LINK
,
1025 true /* is_renderer_init */);
1026 host
= NavigateToEntry(manager
, entry2
);
1028 // The RenderFrameHost created in Init will be reused.
1029 EXPECT_TRUE(host
== manager
->current_frame_host());
1030 EXPECT_FALSE(GetPendingFrameHost(manager
));
1033 manager
->DidNavigateFrame(host
, true);
1034 EXPECT_TRUE(host
== manager
->current_frame_host());
1036 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
1038 // 3) Cross-site navigate to next site. --------------
1039 const GURL
kUrl3("http://webkit.org/");
1040 NavigationEntryImpl
entry3(
1041 NULL
/* instance */, -1 /* page_id */, kUrl3
,
1042 Referrer(kUrl2
, blink::WebReferrerPolicyDefault
),
1043 base::string16() /* title */, ui::PAGE_TRANSITION_LINK
,
1044 false /* is_renderer_init */);
1045 host
= NavigateToEntry(manager
, entry3
);
1047 // A new RenderFrameHost should be created.
1048 EXPECT_TRUE(GetPendingFrameHost(manager
));
1049 ASSERT_EQ(host
, GetPendingFrameHost(manager
));
1051 change_observer
.Reset();
1054 manager
->DidNavigateFrame(GetPendingFrameHost(manager
), true);
1055 EXPECT_TRUE(host
== manager
->current_frame_host());
1057 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
1058 // Check the pending RenderFrameHost has been committed.
1059 EXPECT_FALSE(GetPendingFrameHost(manager
));
1061 // We should observe RVH changed event.
1062 EXPECT_TRUE(change_observer
.DidHostChange());
1065 // Tests WebUI creation.
1066 TEST_F(RenderFrameHostManagerTest
, WebUI
) {
1067 set_should_create_webui(true);
1068 SiteInstance
* instance
= SiteInstance::Create(browser_context());
1070 scoped_ptr
<TestWebContents
> web_contents(
1071 TestWebContents::Create(browser_context(), instance
));
1072 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
1073 RenderFrameHostImpl
* initial_rfh
= manager
->current_frame_host();
1075 EXPECT_FALSE(manager
->current_host()->IsRenderViewLive());
1076 EXPECT_FALSE(manager
->web_ui());
1077 EXPECT_TRUE(initial_rfh
);
1079 const GURL
kUrl("chrome://foo");
1080 NavigationEntryImpl
entry(NULL
/* instance */, -1 /* page_id */, kUrl
,
1081 Referrer(), base::string16() /* title */,
1082 ui::PAGE_TRANSITION_TYPED
,
1083 false /* is_renderer_init */);
1084 RenderFrameHostImpl
* host
= NavigateToEntry(manager
, entry
);
1086 // We commit the pending RenderFrameHost immediately because the previous
1087 // RenderFrameHost was not live. We test a case where it is live in
1090 EXPECT_NE(initial_rfh
, host
);
1091 EXPECT_EQ(host
, manager
->current_frame_host());
1092 EXPECT_FALSE(GetPendingFrameHost(manager
));
1094 // It's important that the SiteInstance get set on the Web UI page as soon
1095 // as the navigation starts, rather than lazily after it commits, so we don't
1096 // try to re-use the SiteInstance/process for non Web UI things that may
1097 // get loaded in between.
1098 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
1099 EXPECT_EQ(kUrl
, host
->GetSiteInstance()->GetSiteURL());
1101 // The Web UI is committed immediately because the RenderViewHost has not been
1102 // used yet. UpdateStateForNavigate() took the short cut path.
1103 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1104 switches::kEnableBrowserSideNavigation
)) {
1105 EXPECT_FALSE(manager
->speculative_web_ui());
1107 EXPECT_FALSE(manager
->pending_web_ui());
1109 EXPECT_TRUE(manager
->web_ui());
1112 manager
->DidNavigateFrame(host
, true);
1114 host
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1117 // Tests that we can open a WebUI link in a new tab from a WebUI page and still
1118 // grant the correct bindings. http://crbug.com/189101.
1119 TEST_F(RenderFrameHostManagerTest
, WebUIInNewTab
) {
1120 set_should_create_webui(true);
1121 SiteInstance
* blank_instance
= SiteInstance::Create(browser_context());
1122 blank_instance
->GetProcess()->Init();
1124 // Create a blank tab.
1125 scoped_ptr
<TestWebContents
> web_contents1(
1126 TestWebContents::Create(browser_context(), blank_instance
));
1127 RenderFrameHostManager
* manager1
=
1128 web_contents1
->GetRenderManagerForTesting();
1129 // Test the case that new RVH is considered live.
1130 manager1
->current_host()->CreateRenderView(-1, MSG_ROUTING_NONE
, -1,
1131 FrameReplicationState(), false);
1132 EXPECT_TRUE(manager1
->current_host()->IsRenderViewLive());
1133 EXPECT_TRUE(manager1
->current_frame_host()->IsRenderFrameLive());
1135 // Navigate to a WebUI page.
1136 const GURL
kUrl1("chrome://foo");
1137 NavigationEntryImpl
entry1(NULL
/* instance */, -1 /* page_id */, kUrl1
,
1138 Referrer(), base::string16() /* title */,
1139 ui::PAGE_TRANSITION_TYPED
,
1140 false /* is_renderer_init */);
1141 RenderFrameHostImpl
* host1
= NavigateToEntry(manager1
, entry1
);
1143 // We should have a pending navigation to the WebUI RenderViewHost.
1144 // It should already have bindings.
1145 EXPECT_EQ(host1
, GetPendingFrameHost(manager1
));
1146 EXPECT_NE(host1
, manager1
->current_frame_host());
1148 host1
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1150 // Commit and ensure we still have bindings.
1151 manager1
->DidNavigateFrame(host1
, true);
1152 SiteInstance
* webui_instance
= host1
->GetSiteInstance();
1153 EXPECT_EQ(host1
, manager1
->current_frame_host());
1155 host1
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1157 // Now simulate clicking a link that opens in a new tab.
1158 scoped_ptr
<TestWebContents
> web_contents2(
1159 TestWebContents::Create(browser_context(), webui_instance
));
1160 RenderFrameHostManager
* manager2
=
1161 web_contents2
->GetRenderManagerForTesting();
1162 // Make sure the new RVH is considered live. This is usually done in
1163 // RenderWidgetHost::Init when opening a new tab from a link.
1164 manager2
->current_host()->CreateRenderView(-1, MSG_ROUTING_NONE
, -1,
1165 FrameReplicationState(), false);
1166 EXPECT_TRUE(manager2
->current_host()->IsRenderViewLive());
1168 const GURL
kUrl2("chrome://foo/bar");
1169 NavigationEntryImpl
entry2(NULL
/* instance */, -1 /* page_id */, kUrl2
,
1170 Referrer(), base::string16() /* title */,
1171 ui::PAGE_TRANSITION_LINK
,
1172 true /* is_renderer_init */);
1173 RenderFrameHostImpl
* host2
= NavigateToEntry(manager2
, entry2
);
1175 // No cross-process transition happens because we are already in the right
1176 // SiteInstance. We should grant bindings immediately.
1177 EXPECT_EQ(host2
, manager2
->current_frame_host());
1178 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1179 switches::kEnableBrowserSideNavigation
)) {
1180 EXPECT_TRUE(manager2
->speculative_web_ui());
1182 EXPECT_TRUE(manager2
->pending_web_ui());
1185 host2
->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1187 manager2
->DidNavigateFrame(host2
, true);
1190 // Tests that a WebUI is correctly reused between chrome:// pages.
1191 TEST_F(RenderFrameHostManagerTest
, WebUIWasReused
) {
1192 set_should_create_webui(true);
1194 // Navigate to a WebUI page.
1195 const GURL
kUrl1("chrome://foo");
1196 contents()->NavigateAndCommit(kUrl1
);
1197 RenderFrameHostManager
* manager
=
1198 main_test_rfh()->frame_tree_node()->render_manager();
1199 WebUIImpl
* web_ui
= manager
->web_ui();
1200 EXPECT_TRUE(web_ui
);
1202 // Navigate to another WebUI page which should be same-site and keep the
1204 const GURL
kUrl2("chrome://foo/bar");
1205 contents()->NavigateAndCommit(kUrl2
);
1206 EXPECT_EQ(web_ui
, manager
->web_ui());
1209 // Tests that a WebUI is correctly cleaned up when navigating from a chrome://
1210 // page to a non-chrome:// page.
1211 TEST_F(RenderFrameHostManagerTest
, WebUIWasCleared
) {
1212 set_should_create_webui(true);
1214 // Navigate to a WebUI page.
1215 const GURL
kUrl1("chrome://foo");
1216 contents()->NavigateAndCommit(kUrl1
);
1217 EXPECT_TRUE(main_test_rfh()->frame_tree_node()->render_manager()->web_ui());
1219 // Navigate to a non-WebUI page.
1220 const GURL
kUrl2("http://www.google.com");
1221 contents()->NavigateAndCommit(kUrl2
);
1222 EXPECT_FALSE(main_test_rfh()->frame_tree_node()->render_manager()->web_ui());
1225 // Tests that we don't end up in an inconsistent state if a page does a back and
1226 // then reload. http://crbug.com/51680
1227 // Also tests that only user-gesture navigations can interrupt cross-process
1228 // navigations. http://crbug.com/75195
1229 TEST_F(RenderFrameHostManagerTest
, PageDoesBackAndReload
) {
1230 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1231 switches::kEnableBrowserSideNavigation
)) {
1232 // PlzNavigate uses a significantly different logic for renderer initiated
1233 // navigations and navigation cancellation. Adapting this test would make it
1234 // full of special cases and almost unreadable.
1235 // There are tests that exercise these concerns for PlzNavigate, all from
1236 // NavigatorTestWithBrowserSideNavigation:
1237 // - BrowserInitiatedNavigationCancel
1238 // - RendererUserInitiatedNavigationCancel
1239 // - RendererNonUserInitiatedNavigationDoesntCancelRendererUserInitiated
1240 // - RendererNonUserInitiatedNavigationDoesntCancelBrowserInitiated
1241 // - RendererNonUserInitiatedNavigationCancelSimilarNavigation
1242 SUCCEED() << "Test is not applicable with browser side navigation enabled";
1245 const GURL
kUrl1("http://www.google.com/");
1246 const GURL
kUrl2("http://www.evil-site.com/");
1248 // Navigate to a safe site, then an evil site.
1249 // This will switch RenderFrameHosts. We cannot assert that the first and
1250 // second RFHs are different, though, because the first one may be promptly
1252 contents()->NavigateAndCommit(kUrl1
);
1253 contents()->NavigateAndCommit(kUrl2
);
1254 TestRenderFrameHost
* evil_rfh
= contents()->GetMainFrame();
1256 // Now let's simulate the evil page calling history.back().
1257 contents()->OnGoToEntryAtOffset(-1);
1258 contents()->GetMainFrame()->PrepareForCommit();
1259 // We should have a new pending RFH.
1260 // Note that in this case, the navigation has not committed, so evil_rfh will
1261 // not be deleted yet.
1262 EXPECT_NE(evil_rfh
, contents()->GetPendingMainFrame());
1263 EXPECT_NE(evil_rfh
->GetRenderViewHost(),
1264 contents()->GetPendingMainFrame()->GetRenderViewHost());
1266 // Before that RFH has committed, the evil page reloads itself.
1267 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1269 params
.nav_entry_id
= 0;
1270 params
.did_create_new_entry
= false;
1272 params
.transition
= ui::PAGE_TRANSITION_CLIENT_REDIRECT
;
1273 params
.should_update_history
= false;
1274 params
.gesture
= NavigationGestureAuto
;
1275 params
.was_within_same_page
= false;
1276 params
.is_post
= false;
1277 params
.page_state
= PageState::CreateFromURL(kUrl2
);
1279 evil_rfh
->SimulateNavigationStart(kUrl2
);
1280 evil_rfh
->SendNavigateWithParams(¶ms
);
1281 evil_rfh
->SimulateNavigationStop();
1283 // That should NOT have cancelled the pending RFH, because the reload did
1284 // not have a user gesture. Thus, the pending back navigation will still
1285 // eventually commit.
1286 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
1287 pending_render_view_host() != NULL
);
1288 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->pending_frame_host() !=
1291 contents()->GetRenderManagerForTesting()->current_frame_host());
1292 EXPECT_EQ(evil_rfh
->GetRenderViewHost(),
1293 contents()->GetRenderManagerForTesting()->current_host());
1295 // Also we should not have a pending navigation entry.
1296 EXPECT_TRUE(contents()->GetController().GetPendingEntry() == NULL
);
1297 NavigationEntry
* entry
= contents()->GetController().GetVisibleEntry();
1298 ASSERT_TRUE(entry
!= NULL
);
1299 EXPECT_EQ(kUrl2
, entry
->GetURL());
1301 // Now do the same but as a user gesture.
1302 params
.gesture
= NavigationGestureUser
;
1303 evil_rfh
->SimulateNavigationStart(kUrl2
);
1304 evil_rfh
->SendNavigateWithParams(¶ms
);
1305 evil_rfh
->SimulateNavigationStop();
1307 // User navigation should have cancelled the pending RFH.
1308 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
1309 pending_render_view_host() == NULL
);
1310 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->pending_frame_host() ==
1313 contents()->GetRenderManagerForTesting()->current_frame_host());
1314 EXPECT_EQ(evil_rfh
->GetRenderViewHost(),
1315 contents()->GetRenderManagerForTesting()->current_host());
1317 // Also we should not have a pending navigation entry.
1318 EXPECT_TRUE(contents()->GetController().GetPendingEntry() == NULL
);
1319 entry
= contents()->GetController().GetVisibleEntry();
1320 ASSERT_TRUE(entry
!= NULL
);
1321 EXPECT_EQ(kUrl2
, entry
->GetURL());
1324 // Ensure that we can go back and forward even if a SwapOut ACK isn't received.
1325 // See http://crbug.com/93427.
1326 TEST_F(RenderFrameHostManagerTest
, NavigateAfterMissingSwapOutACK
) {
1327 const GURL
kUrl1("http://www.google.com/");
1328 const GURL
kUrl2("http://www.chromium.org/");
1330 // Navigate to two pages.
1331 contents()->NavigateAndCommit(kUrl1
);
1332 TestRenderFrameHost
* rfh1
= main_test_rfh();
1334 // Keep active_frame_count nonzero so that no swapped out frames in
1335 // this SiteInstance get forcefully deleted.
1336 rfh1
->GetSiteInstance()->increment_active_frame_count();
1338 contents()->NavigateAndCommit(kUrl2
);
1339 TestRenderFrameHost
* rfh2
= main_test_rfh();
1340 rfh2
->GetSiteInstance()->increment_active_frame_count();
1342 // Now go back, but suppose the SwapOut_ACK isn't received. This shouldn't
1343 // happen, but we have seen it when going back quickly across many entries
1344 // (http://crbug.com/93427).
1345 contents()->GetController().GoBack();
1346 EXPECT_TRUE(rfh2
->is_waiting_for_beforeunload_ack());
1347 contents()->GetMainFrame()->PrepareForCommit();
1348 EXPECT_FALSE(rfh2
->is_waiting_for_beforeunload_ack());
1350 // The back navigation commits.
1351 const NavigationEntry
* entry1
= contents()->GetController().GetPendingEntry();
1352 contents()->GetPendingMainFrame()->SendNavigate(
1353 entry1
->GetPageID(), entry1
->GetUniqueID(), false, entry1
->GetURL());
1354 EXPECT_TRUE(rfh2
->IsWaitingForUnloadACK());
1355 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh2
->rfh_state());
1357 // We should be able to navigate forward.
1358 contents()->GetController().GoForward();
1359 contents()->GetMainFrame()->PrepareForCommit();
1360 const NavigationEntry
* entry2
= contents()->GetController().GetPendingEntry();
1361 contents()->GetPendingMainFrame()->SendNavigate(
1362 entry2
->GetPageID(), entry2
->GetUniqueID(), false, entry2
->GetURL());
1363 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, main_test_rfh()->rfh_state());
1364 if (!SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
1365 EXPECT_EQ(rfh2
, main_test_rfh());
1366 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
1367 rfh1
->OnSwappedOut();
1368 EXPECT_TRUE(rfh1
->is_swapped_out());
1369 EXPECT_EQ(RenderFrameHostImpl::STATE_SWAPPED_OUT
, rfh1
->rfh_state());
1373 // Test that we create swapped out RFHs for the opener chain when navigating an
1374 // opened tab cross-process. This allows us to support certain cross-process
1375 // JavaScript calls (http://crbug.com/99202).
1376 TEST_F(RenderFrameHostManagerTest
, CreateSwappedOutOpenerRFHs
) {
1377 const GURL
kUrl1("http://www.google.com/");
1378 const GURL
kUrl2("http://www.chromium.org/");
1379 const GURL
kChromeUrl("chrome://foo");
1381 // Navigate to an initial URL.
1382 contents()->NavigateAndCommit(kUrl1
);
1383 RenderFrameHostManager
* manager
= contents()->GetRenderManagerForTesting();
1384 TestRenderFrameHost
* rfh1
= main_test_rfh();
1385 scoped_refptr
<SiteInstanceImpl
> site_instance1
= rfh1
->GetSiteInstance();
1386 RenderFrameDeletedObserver
rfh1_deleted_observer(rfh1
);
1387 TestRenderViewHost
* rvh1
= test_rvh();
1389 // Create 2 new tabs and simulate them being the opener chain for the main
1390 // tab. They should be in the same SiteInstance.
1391 scoped_ptr
<TestWebContents
> opener1(
1392 TestWebContents::Create(browser_context(), site_instance1
.get()));
1393 RenderFrameHostManager
* opener1_manager
=
1394 opener1
->GetRenderManagerForTesting();
1395 contents()->SetOpener(opener1
.get());
1397 scoped_ptr
<TestWebContents
> opener2(
1398 TestWebContents::Create(browser_context(), site_instance1
.get()));
1399 RenderFrameHostManager
* opener2_manager
=
1400 opener2
->GetRenderManagerForTesting();
1401 opener1
->SetOpener(opener2
.get());
1403 // Navigate to a cross-site URL (different SiteInstance but same
1404 // BrowsingInstance).
1405 contents()->NavigateAndCommit(kUrl2
);
1406 TestRenderFrameHost
* rfh2
= main_test_rfh();
1407 TestRenderViewHost
* rvh2
= test_rvh();
1408 EXPECT_NE(site_instance1
, rfh2
->GetSiteInstance());
1409 EXPECT_TRUE(site_instance1
->IsRelatedSiteInstance(rfh2
->GetSiteInstance()));
1411 // Ensure rvh1 is placed on swapped out list of the current tab.
1412 if (!SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
1413 EXPECT_TRUE(manager
->IsRVHOnSwappedOutList(rvh1
));
1414 EXPECT_FALSE(rfh1_deleted_observer
.deleted());
1415 EXPECT_TRUE(manager
->IsOnSwappedOutList(rfh1
));
1417 manager
->GetRenderFrameProxyHost(site_instance1
.get())
1418 ->render_frame_host());
1420 EXPECT_TRUE(rfh1_deleted_observer
.deleted());
1421 EXPECT_TRUE(manager
->GetRenderFrameProxyHost(site_instance1
.get()));
1424 manager
->GetSwappedOutRenderViewHost(rvh1
->GetSiteInstance()));
1426 // Ensure a swapped out RFH and RFH is created in the first opener tab.
1427 RenderFrameProxyHost
* opener1_proxy
=
1428 opener1_manager
->GetRenderFrameProxyHost(rfh2
->GetSiteInstance());
1429 RenderFrameHostImpl
* opener1_rfh
= opener1_proxy
->render_frame_host();
1430 TestRenderViewHost
* opener1_rvh
= static_cast<TestRenderViewHost
*>(
1431 opener1_manager
->GetSwappedOutRenderViewHost(rvh2
->GetSiteInstance()));
1432 if (!SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
1433 EXPECT_TRUE(opener1_manager
->IsOnSwappedOutList(opener1_rfh
));
1434 EXPECT_TRUE(opener1_manager
->IsRVHOnSwappedOutList(opener1_rvh
));
1435 EXPECT_TRUE(opener1_rfh
->is_swapped_out());
1437 EXPECT_FALSE(opener1_rfh
);
1439 EXPECT_FALSE(opener1_rvh
->is_active());
1441 // Ensure a swapped out RFH and RVH is created in the second opener tab.
1442 RenderFrameProxyHost
* opener2_proxy
=
1443 opener2_manager
->GetRenderFrameProxyHost(rfh2
->GetSiteInstance());
1444 RenderFrameHostImpl
* opener2_rfh
= opener2_proxy
->render_frame_host();
1445 TestRenderViewHost
* opener2_rvh
= static_cast<TestRenderViewHost
*>(
1446 opener2_manager
->GetSwappedOutRenderViewHost(rvh2
->GetSiteInstance()));
1447 if (!SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
1448 EXPECT_TRUE(opener2_manager
->IsOnSwappedOutList(opener2_rfh
));
1449 EXPECT_TRUE(opener2_manager
->IsRVHOnSwappedOutList(opener2_rvh
));
1450 EXPECT_TRUE(opener2_rfh
->is_swapped_out());
1452 EXPECT_FALSE(opener2_rfh
);
1454 EXPECT_FALSE(opener2_rvh
->is_active());
1456 // Navigate to a cross-BrowsingInstance URL.
1457 contents()->NavigateAndCommit(kChromeUrl
);
1458 TestRenderFrameHost
* rfh3
= main_test_rfh();
1459 EXPECT_NE(site_instance1
, rfh3
->GetSiteInstance());
1460 EXPECT_FALSE(site_instance1
->IsRelatedSiteInstance(rfh3
->GetSiteInstance()));
1462 // No scripting is allowed across BrowsingInstances, so we should not create
1463 // swapped out RVHs for the opener chain in this case.
1464 EXPECT_FALSE(opener1_manager
->GetRenderFrameProxyHost(
1465 rfh3
->GetSiteInstance()));
1466 EXPECT_FALSE(opener1_manager
->GetSwappedOutRenderViewHost(
1467 rfh3
->GetSiteInstance()));
1468 EXPECT_FALSE(opener2_manager
->GetRenderFrameProxyHost(
1469 rfh3
->GetSiteInstance()));
1470 EXPECT_FALSE(opener2_manager
->GetSwappedOutRenderViewHost(
1471 rfh3
->GetSiteInstance()));
1474 // Test that a page can disown the opener of the WebContents.
1475 TEST_F(RenderFrameHostManagerTest
, DisownOpener
) {
1476 const GURL
kUrl1("http://www.google.com/");
1477 const GURL
kUrl2("http://www.chromium.org/");
1479 // Navigate to an initial URL.
1480 contents()->NavigateAndCommit(kUrl1
);
1481 TestRenderFrameHost
* rfh1
= main_test_rfh();
1482 scoped_refptr
<SiteInstanceImpl
> site_instance1
= rfh1
->GetSiteInstance();
1484 // Create a new tab and simulate having it be the opener for the main tab.
1485 scoped_ptr
<TestWebContents
> opener1(
1486 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1487 contents()->SetOpener(opener1
.get());
1488 EXPECT_TRUE(contents()->HasOpener());
1490 // Navigate to a cross-site URL (different SiteInstance but same
1491 // BrowsingInstance).
1492 contents()->NavigateAndCommit(kUrl2
);
1493 TestRenderFrameHost
* rfh2
= main_test_rfh();
1494 EXPECT_NE(site_instance1
, rfh2
->GetSiteInstance());
1496 // Disown the opener from rfh2.
1497 rfh2
->DidDisownOpener();
1499 // Ensure the opener is cleared.
1500 EXPECT_FALSE(contents()->HasOpener());
1503 // Test that a page can disown a same-site opener of the WebContents.
1504 TEST_F(RenderFrameHostManagerTest
, DisownSameSiteOpener
) {
1505 const GURL
kUrl1("http://www.google.com/");
1507 // Navigate to an initial URL.
1508 contents()->NavigateAndCommit(kUrl1
);
1509 TestRenderFrameHost
* rfh1
= main_test_rfh();
1511 // Create a new tab and simulate having it be the opener for the main tab.
1512 scoped_ptr
<TestWebContents
> opener1(
1513 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1514 contents()->SetOpener(opener1
.get());
1515 EXPECT_TRUE(contents()->HasOpener());
1517 // Disown the opener from rfh1.
1518 rfh1
->DidDisownOpener();
1520 // Ensure the opener is cleared even if it is in the same process.
1521 EXPECT_FALSE(contents()->HasOpener());
1524 // Test that a page can disown the opener just as a cross-process navigation is
1526 TEST_F(RenderFrameHostManagerTest
, DisownOpenerDuringNavigation
) {
1527 const GURL
kUrl1("http://www.google.com/");
1528 const GURL
kUrl2("http://www.chromium.org/");
1530 // Navigate to an initial URL.
1531 contents()->NavigateAndCommit(kUrl1
);
1532 scoped_refptr
<SiteInstanceImpl
> site_instance1
=
1533 main_test_rfh()->GetSiteInstance();
1535 // Create a new tab and simulate having it be the opener for the main tab.
1536 scoped_ptr
<TestWebContents
> opener1(
1537 TestWebContents::Create(browser_context(), site_instance1
.get()));
1538 contents()->SetOpener(opener1
.get());
1539 EXPECT_TRUE(contents()->HasOpener());
1541 // Navigate to a cross-site URL (different SiteInstance but same
1542 // BrowsingInstance).
1543 contents()->NavigateAndCommit(kUrl2
);
1544 TestRenderFrameHost
* rfh2
= main_test_rfh();
1545 EXPECT_NE(site_instance1
, rfh2
->GetSiteInstance());
1547 // Start a back navigation.
1548 contents()->GetController().GoBack();
1549 contents()->GetMainFrame()->PrepareForCommit();
1551 // Disown the opener from rfh2.
1552 rfh2
->DidDisownOpener();
1554 // Ensure the opener is cleared.
1555 EXPECT_FALSE(contents()->HasOpener());
1557 // The back navigation commits.
1558 const NavigationEntry
* entry1
= contents()->GetController().GetPendingEntry();
1559 contents()->GetPendingMainFrame()->SendNavigate(
1560 entry1
->GetPageID(), entry1
->GetUniqueID(), false, entry1
->GetURL());
1562 // Ensure the opener is still cleared.
1563 EXPECT_FALSE(contents()->HasOpener());
1566 // Test that a page can disown the opener just after a cross-process navigation
1568 TEST_F(RenderFrameHostManagerTest
, DisownOpenerAfterNavigation
) {
1569 const GURL
kUrl1("http://www.google.com/");
1570 const GURL
kUrl2("http://www.chromium.org/");
1572 // Navigate to an initial URL.
1573 contents()->NavigateAndCommit(kUrl1
);
1574 scoped_refptr
<SiteInstanceImpl
> site_instance1
=
1575 main_test_rfh()->GetSiteInstance();
1577 // Create a new tab and simulate having it be the opener for the main tab.
1578 scoped_ptr
<TestWebContents
> opener1(
1579 TestWebContents::Create(browser_context(), site_instance1
.get()));
1580 contents()->SetOpener(opener1
.get());
1581 EXPECT_TRUE(contents()->HasOpener());
1583 // Navigate to a cross-site URL (different SiteInstance but same
1584 // BrowsingInstance).
1585 contents()->NavigateAndCommit(kUrl2
);
1586 TestRenderFrameHost
* rfh2
= main_test_rfh();
1587 EXPECT_NE(site_instance1
, rfh2
->GetSiteInstance());
1589 // Commit a back navigation before the DidDisownOpener message arrives.
1590 contents()->GetController().GoBack();
1591 contents()->GetMainFrame()->PrepareForCommit();
1592 const NavigationEntry
* entry1
= contents()->GetController().GetPendingEntry();
1593 contents()->GetPendingMainFrame()->SendNavigate(
1594 entry1
->GetPageID(), entry1
->GetUniqueID(), false, entry1
->GetURL());
1596 // Disown the opener from rfh2.
1597 rfh2
->DidDisownOpener();
1598 EXPECT_FALSE(contents()->HasOpener());
1601 // Test that we clean up swapped out RenderViewHosts when a process hosting
1602 // those associated RenderViews crashes. http://crbug.com/258993
1603 TEST_F(RenderFrameHostManagerTest
, CleanUpSwappedOutRVHOnProcessCrash
) {
1604 const GURL
kUrl1("http://www.google.com/");
1605 const GURL
kUrl2("http://www.chromium.org/");
1607 // Navigate to an initial URL.
1608 contents()->NavigateAndCommit(kUrl1
);
1609 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1611 // Create a new tab as an opener for the main tab.
1612 scoped_ptr
<TestWebContents
> opener1(
1613 TestWebContents::Create(browser_context(), rfh1
->GetSiteInstance()));
1614 RenderFrameHostManager
* opener1_manager
=
1615 opener1
->GetRenderManagerForTesting();
1616 contents()->SetOpener(opener1
.get());
1618 // Make sure the new opener RVH is considered live.
1619 opener1_manager
->current_host()->CreateRenderView(
1620 -1, MSG_ROUTING_NONE
, -1, FrameReplicationState(), false);
1621 EXPECT_TRUE(opener1_manager
->current_host()->IsRenderViewLive());
1622 EXPECT_TRUE(opener1_manager
->current_frame_host()->IsRenderFrameLive());
1624 // Use a cross-process navigation in the opener to swap out the old RVH.
1626 opener1_manager
->GetSwappedOutRenderViewHost(rfh1
->GetSiteInstance()));
1627 opener1
->NavigateAndCommit(kUrl2
);
1628 RenderViewHostImpl
* swapped_out_rvh
=
1629 opener1_manager
->GetSwappedOutRenderViewHost(rfh1
->GetSiteInstance());
1630 EXPECT_TRUE(swapped_out_rvh
);
1631 EXPECT_TRUE(swapped_out_rvh
->is_swapped_out_
);
1632 EXPECT_FALSE(swapped_out_rvh
->is_active());
1634 // Fake a process crash.
1635 rfh1
->GetProcess()->SimulateCrash();
1637 // Ensure that the RenderFrameProxyHost stays around and the RenderFrameProxy
1639 RenderFrameProxyHost
* render_frame_proxy_host
=
1640 opener1_manager
->GetRenderFrameProxyHost(rfh1
->GetSiteInstance());
1641 EXPECT_TRUE(render_frame_proxy_host
);
1642 EXPECT_FALSE(render_frame_proxy_host
->is_render_frame_proxy_live());
1644 // Expect the swapped out RVH to exist but not be live.
1646 opener1_manager
->GetSwappedOutRenderViewHost(rfh1
->GetSiteInstance()));
1648 opener1_manager
->GetSwappedOutRenderViewHost(rfh1
->GetSiteInstance())
1649 ->IsRenderViewLive());
1651 // Reload the initial tab. This should recreate the opener's swapped out RVH
1652 // in the original SiteInstance.
1653 contents()->GetController().Reload(true);
1654 contents()->GetMainFrame()->PrepareForCommit();
1656 opener1_manager
->GetSwappedOutRenderViewHost(rfh1
->GetSiteInstance())
1657 ->IsRenderViewLive());
1659 opener1_manager
->GetRoutingIdForSiteInstance(rfh1
->GetSiteInstance()),
1660 contents()->GetMainFrame()->GetRenderViewHost()->opener_frame_route_id());
1663 // Test that RenderViewHosts created for WebUI navigations are properly
1664 // granted WebUI bindings even if an unprivileged swapped out RenderViewHost
1665 // is in the same process (http://crbug.com/79918).
1666 TEST_F(RenderFrameHostManagerTest
, EnableWebUIWithSwappedOutOpener
) {
1667 set_should_create_webui(true);
1668 const GURL
kSettingsUrl("chrome://chrome/settings");
1669 const GURL
kPluginUrl("chrome://plugins");
1671 // Navigate to an initial WebUI URL.
1672 contents()->NavigateAndCommit(kSettingsUrl
);
1674 // Ensure the RVH has WebUI bindings.
1675 TestRenderViewHost
* rvh1
= test_rvh();
1676 EXPECT_TRUE(rvh1
->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1678 // Create a new tab and simulate it being the opener for the main
1679 // tab. It should be in the same SiteInstance.
1680 scoped_ptr
<TestWebContents
> opener1(
1681 TestWebContents::Create(browser_context(), rvh1
->GetSiteInstance()));
1682 RenderFrameHostManager
* opener1_manager
=
1683 opener1
->GetRenderManagerForTesting();
1684 contents()->SetOpener(opener1
.get());
1686 // Navigate to a different WebUI URL (different SiteInstance, same
1687 // BrowsingInstance).
1688 contents()->NavigateAndCommit(kPluginUrl
);
1689 TestRenderViewHost
* rvh2
= test_rvh();
1690 EXPECT_NE(rvh1
->GetSiteInstance(), rvh2
->GetSiteInstance());
1691 EXPECT_TRUE(rvh1
->GetSiteInstance()->IsRelatedSiteInstance(
1692 rvh2
->GetSiteInstance()));
1694 // Ensure a swapped out RFH and RVH is created in the first opener tab.
1695 RenderFrameProxyHost
* opener1_proxy
=
1696 opener1_manager
->GetRenderFrameProxyHost(rvh2
->GetSiteInstance());
1697 RenderFrameHostImpl
* opener1_rfh
= opener1_proxy
->render_frame_host();
1698 TestRenderViewHost
* opener1_rvh
= static_cast<TestRenderViewHost
*>(
1699 opener1_manager
->GetSwappedOutRenderViewHost(rvh2
->GetSiteInstance()));
1700 if (!SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
1701 EXPECT_TRUE(opener1_manager
->IsOnSwappedOutList(opener1_rfh
));
1702 EXPECT_TRUE(opener1_manager
->IsRVHOnSwappedOutList(opener1_rvh
));
1703 EXPECT_TRUE(opener1_rfh
->is_swapped_out());
1705 EXPECT_FALSE(opener1_rfh
);
1707 EXPECT_FALSE(opener1_rvh
->is_active());
1709 // Ensure the new RVH has WebUI bindings.
1710 EXPECT_TRUE(rvh2
->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI
);
1713 // Test that we reuse the same guest SiteInstance if we navigate across sites.
1714 TEST_F(RenderFrameHostManagerTest
, NoSwapOnGuestNavigations
) {
1715 GURL
guest_url(std::string(kGuestScheme
).append("://abc123"));
1716 SiteInstance
* instance
=
1717 SiteInstance::CreateForURL(browser_context(), guest_url
);
1718 scoped_ptr
<TestWebContents
> web_contents(
1719 TestWebContents::Create(browser_context(), instance
));
1721 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
1723 RenderFrameHostImpl
* host
= NULL
;
1725 // 1) The first navigation. --------------------------
1726 const GURL
kUrl1("http://www.google.com/");
1727 NavigationEntryImpl
entry1(
1728 NULL
/* instance */, -1 /* page_id */, kUrl1
, Referrer(),
1729 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED
,
1730 false /* is_renderer_init */);
1731 host
= NavigateToEntry(manager
, entry1
);
1733 // The RenderFrameHost created in Init will be reused.
1734 EXPECT_TRUE(host
== manager
->current_frame_host());
1735 EXPECT_FALSE(manager
->pending_frame_host());
1736 EXPECT_EQ(manager
->current_frame_host()->GetSiteInstance(), instance
);
1739 manager
->DidNavigateFrame(host
, true);
1740 // Commit to SiteInstance should be delayed until RenderFrame commit.
1741 EXPECT_EQ(host
, manager
->current_frame_host());
1743 EXPECT_TRUE(host
->GetSiteInstance()->HasSite());
1745 // 2) Navigate to a different domain. -------------------------
1746 // Guests stay in the same process on navigation.
1747 const GURL
kUrl2("http://www.chromium.org");
1748 NavigationEntryImpl
entry2(
1749 NULL
/* instance */, -1 /* page_id */, kUrl2
,
1750 Referrer(kUrl1
, blink::WebReferrerPolicyDefault
),
1751 base::string16() /* title */, ui::PAGE_TRANSITION_LINK
,
1752 true /* is_renderer_init */);
1753 host
= NavigateToEntry(manager
, entry2
);
1755 // The RenderFrameHost created in Init will be reused.
1756 EXPECT_EQ(host
, manager
->current_frame_host());
1757 EXPECT_FALSE(manager
->pending_frame_host());
1760 manager
->DidNavigateFrame(host
, true);
1761 EXPECT_EQ(host
, manager
->current_frame_host());
1763 EXPECT_EQ(host
->GetSiteInstance(), instance
);
1766 // Test that we cancel a pending RVH if we close the tab while it's pending.
1767 // http://crbug.com/294697.
1768 TEST_F(RenderFrameHostManagerTest
, NavigateWithEarlyClose
) {
1769 TestNotificationTracker notifications
;
1771 SiteInstance
* instance
= SiteInstance::Create(browser_context());
1773 BeforeUnloadFiredWebContentsDelegate delegate
;
1774 scoped_ptr
<TestWebContents
> web_contents(
1775 TestWebContents::Create(browser_context(), instance
));
1776 RenderViewHostChangedObserver
change_observer(web_contents
.get());
1777 web_contents
->SetDelegate(&delegate
);
1779 RenderFrameHostManager
* manager
= web_contents
->GetRenderManagerForTesting();
1781 // 1) The first navigation. --------------------------
1782 const GURL
kUrl1("http://www.google.com/");
1783 NavigationEntryImpl
entry1(NULL
/* instance */, -1 /* page_id */, kUrl1
,
1784 Referrer(), base::string16() /* title */,
1785 ui::PAGE_TRANSITION_TYPED
,
1786 false /* is_renderer_init */);
1787 RenderFrameHostImpl
* host
= NavigateToEntry(manager
, entry1
);
1789 // The RenderFrameHost created in Init will be reused.
1790 EXPECT_EQ(host
, manager
->current_frame_host());
1791 EXPECT_FALSE(GetPendingFrameHost(manager
));
1793 // We should observe RVH changed event.
1794 EXPECT_TRUE(change_observer
.DidHostChange());
1797 manager
->DidNavigateFrame(host
, true);
1799 // Commit to SiteInstance should be delayed until RenderFrame commits.
1800 EXPECT_EQ(host
, manager
->current_frame_host());
1801 EXPECT_FALSE(host
->GetSiteInstance()->HasSite());
1802 host
->GetSiteInstance()->SetSite(kUrl1
);
1804 // 2) Cross-site navigate to next site. -------------------------
1805 const GURL
kUrl2("http://www.example.com");
1806 NavigationEntryImpl
entry2(
1807 NULL
/* instance */, -1 /* page_id */, kUrl2
, Referrer(),
1808 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED
,
1809 false /* is_renderer_init */);
1810 RenderFrameHostImpl
* host2
= NavigateToEntry(manager
, entry2
);
1812 // A new RenderFrameHost should be created.
1813 ASSERT_EQ(host2
, GetPendingFrameHost(manager
));
1814 EXPECT_NE(host2
, host
);
1816 EXPECT_EQ(host
, manager
->current_frame_host());
1817 EXPECT_FALSE(manager
->current_frame_host()->is_swapped_out());
1818 EXPECT_EQ(host2
, GetPendingFrameHost(manager
));
1820 // 3) Close the tab. -------------------------
1821 notifications
.ListenFor(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED
,
1822 Source
<RenderWidgetHost
>(host2
->render_view_host()));
1823 manager
->OnBeforeUnloadACK(false, true, base::TimeTicks());
1826 notifications
.Check1AndReset(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED
));
1827 EXPECT_FALSE(GetPendingFrameHost(manager
));
1828 EXPECT_EQ(host
, manager
->current_frame_host());
1831 TEST_F(RenderFrameHostManagerTest
, CloseWithPendingWhileUnresponsive
) {
1832 const GURL
kUrl1("http://www.google.com/");
1833 const GURL
kUrl2("http://www.chromium.org/");
1835 CloseWebContentsDelegate close_delegate
;
1836 contents()->SetDelegate(&close_delegate
);
1838 // Navigate to the first page.
1839 contents()->NavigateAndCommit(kUrl1
);
1840 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1842 // Start to close the tab, but assume it's unresponsive.
1843 rfh1
->render_view_host()->ClosePage();
1844 EXPECT_TRUE(rfh1
->render_view_host()->is_waiting_for_close_ack());
1846 // Start a navigation to a new site.
1847 controller().LoadURL(
1848 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1849 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1850 switches::kEnableBrowserSideNavigation
)) {
1851 rfh1
->PrepareForCommit();
1853 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1855 // Simulate the unresponsiveness timer. The tab should close.
1856 contents()->RendererUnresponsive(rfh1
->render_view_host());
1857 EXPECT_TRUE(close_delegate
.is_closed());
1860 // Tests that the RenderFrameHost is properly deleted when the SwapOutACK is
1861 // received. (SwapOut and the corresponding ACK always occur after commit.)
1862 // Also tests that an early SwapOutACK is properly ignored.
1863 TEST_F(RenderFrameHostManagerTest
, DeleteFrameAfterSwapOutACK
) {
1864 const GURL
kUrl1("http://www.google.com/");
1865 const GURL
kUrl2("http://www.chromium.org/");
1867 // Navigate to the first page.
1868 contents()->NavigateAndCommit(kUrl1
);
1869 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1870 RenderFrameDeletedObserver
rfh_deleted_observer(rfh1
);
1871 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1873 // Navigate to new site, simulating onbeforeunload approval.
1874 controller().LoadURL(
1875 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1876 int entry_id
= controller().GetPendingEntry()->GetUniqueID();
1877 contents()->GetMainFrame()->PrepareForCommit();
1878 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1879 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1880 TestRenderFrameHost
* rfh2
= contents()->GetPendingMainFrame();
1882 // Simulate the swap out ack, unexpectedly early (before commit). It should
1884 rfh1
->OnSwappedOut();
1885 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1886 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1888 // The new page commits.
1889 contents()->TestDidNavigate(rfh2
, 1, entry_id
, true, kUrl2
,
1890 ui::PAGE_TRANSITION_TYPED
);
1891 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1892 EXPECT_EQ(rfh2
, contents()->GetMainFrame());
1893 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL
);
1894 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
1895 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
1897 rfh1
->frame_tree_node()->render_manager()->IsPendingDeletion(rfh1
));
1899 // Simulate the swap out ack.
1900 rfh1
->OnSwappedOut();
1902 // rfh1 should have been deleted.
1903 EXPECT_TRUE(rfh_deleted_observer
.deleted());
1907 // Tests that the RenderFrameHost is properly swapped out when the SwapOut ACK
1908 // is received. (SwapOut and the corresponding ACK always occur after commit.)
1909 TEST_F(RenderFrameHostManagerTest
, SwapOutFrameAfterSwapOutACK
) {
1910 const GURL
kUrl1("http://www.google.com/");
1911 const GURL
kUrl2("http://www.chromium.org/");
1913 // Navigate to the first page.
1914 contents()->NavigateAndCommit(kUrl1
);
1915 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1916 RenderFrameDeletedObserver
rfh_deleted_observer(rfh1
);
1917 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1919 // Increment the number of active frames in SiteInstanceImpl so that rfh1 is
1920 // not deleted on swap out.
1921 rfh1
->GetSiteInstance()->increment_active_frame_count();
1923 // Navigate to new site, simulating onbeforeunload approval.
1924 controller().LoadURL(
1925 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1926 int entry_id
= controller().GetPendingEntry()->GetUniqueID();
1927 contents()->GetMainFrame()->PrepareForCommit();
1928 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1929 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1930 TestRenderFrameHost
* rfh2
= contents()->GetPendingMainFrame();
1932 // The new page commits.
1933 contents()->TestDidNavigate(rfh2
, 1, entry_id
, true, kUrl2
,
1934 ui::PAGE_TRANSITION_TYPED
);
1935 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1936 EXPECT_EQ(rfh2
, contents()->GetMainFrame());
1937 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL
);
1938 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
1939 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
1941 // Simulate the swap out ack.
1942 rfh1
->OnSwappedOut();
1944 // rfh1 should be swapped out or deleted in --site-per-process.
1945 if (!SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
1946 EXPECT_FALSE(rfh_deleted_observer
.deleted());
1947 EXPECT_TRUE(rfh1
->is_swapped_out());
1949 EXPECT_TRUE(rfh_deleted_observer
.deleted());
1953 // Test that the RenderViewHost is properly swapped out if a navigation in the
1954 // new renderer commits before sending the SwapOut message to the old renderer.
1955 // This simulates a cross-site navigation to a synchronously committing URL
1956 // (e.g., a data URL) and ensures it works properly.
1957 TEST_F(RenderFrameHostManagerTest
,
1958 CommitNewNavigationBeforeSendingSwapOut
) {
1959 const GURL
kUrl1("http://www.google.com/");
1960 const GURL
kUrl2("http://www.chromium.org/");
1962 // Navigate to the first page.
1963 contents()->NavigateAndCommit(kUrl1
);
1964 TestRenderFrameHost
* rfh1
= contents()->GetMainFrame();
1965 RenderFrameDeletedObserver
rfh_deleted_observer(rfh1
);
1966 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
1968 // Increment the number of active frames in SiteInstanceImpl so that rfh1 is
1969 // not deleted on swap out.
1970 scoped_refptr
<SiteInstanceImpl
> site_instance
= rfh1
->GetSiteInstance();
1971 site_instance
->increment_active_frame_count();
1973 // Navigate to new site, simulating onbeforeunload approval.
1974 controller().LoadURL(
1975 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1976 int entry_id
= controller().GetPendingEntry()->GetUniqueID();
1977 rfh1
->PrepareForCommit();
1978 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1979 TestRenderFrameHost
* rfh2
= contents()->GetPendingMainFrame();
1981 // The new page commits.
1982 contents()->TestDidNavigate(rfh2
, 1, entry_id
, true, kUrl2
,
1983 ui::PAGE_TRANSITION_TYPED
);
1984 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1985 EXPECT_EQ(rfh2
, contents()->GetMainFrame());
1986 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL
);
1987 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh2
->rfh_state());
1988 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT
, rfh1
->rfh_state());
1990 // Simulate the swap out ack.
1991 rfh1
->OnSwappedOut();
1993 // rfh1 should be swapped out.
1994 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
1995 EXPECT_TRUE(rfh_deleted_observer
.deleted());
1996 EXPECT_TRUE(contents()->GetFrameTree()->root()->render_manager()
1997 ->GetRenderFrameProxyHost(site_instance
.get()));
1999 EXPECT_FALSE(rfh_deleted_observer
.deleted());
2000 EXPECT_TRUE(rfh1
->is_swapped_out());
2004 // Test that a RenderFrameHost is properly deleted when a cross-site navigation
2006 TEST_F(RenderFrameHostManagerTest
,
2007 CancelPendingProperlyDeletesOrSwaps
) {
2008 const GURL
kUrl1("http://www.google.com/");
2009 const GURL
kUrl2("http://www.chromium.org/");
2010 RenderFrameHostImpl
* pending_rfh
= NULL
;
2011 base::TimeTicks now
= base::TimeTicks::Now();
2013 // Navigate to the first page.
2014 contents()->NavigateAndCommit(kUrl1
);
2015 TestRenderFrameHost
* rfh1
= main_test_rfh();
2016 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh1
->rfh_state());
2018 // Navigate to a new site, starting a cross-site navigation.
2019 controller().LoadURL(
2020 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
2022 pending_rfh
= contents()->GetPendingMainFrame();
2023 RenderFrameDeletedObserver
rfh_deleted_observer(pending_rfh
);
2025 // Cancel the navigation by simulating a declined beforeunload dialog.
2026 contents()->GetMainFrame()->OnMessageReceived(
2027 FrameHostMsg_BeforeUnload_ACK(0, false, now
, now
));
2028 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
2030 // Since the pending RFH is the only one for the new SiteInstance, it should
2032 EXPECT_TRUE(rfh_deleted_observer
.deleted());
2035 // Start another cross-site navigation.
2036 controller().LoadURL(
2037 kUrl2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
2039 pending_rfh
= contents()->GetPendingMainFrame();
2040 RenderFrameDeletedObserver
rfh_deleted_observer(pending_rfh
);
2042 // Increment the number of active frames in the new SiteInstance, which will
2043 // cause the pending RFH to be deleted and a RenderFrameProxyHost to be
2045 scoped_refptr
<SiteInstanceImpl
> site_instance
=
2046 pending_rfh
->GetSiteInstance();
2047 site_instance
->increment_active_frame_count();
2049 contents()->GetMainFrame()->OnMessageReceived(
2050 FrameHostMsg_BeforeUnload_ACK(0, false, now
, now
));
2051 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
2053 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
2054 EXPECT_TRUE(rfh_deleted_observer
.deleted());
2055 EXPECT_TRUE(contents()->GetFrameTree()->root()->render_manager()
2056 ->GetRenderFrameProxyHost(site_instance
.get()));
2058 EXPECT_FALSE(rfh_deleted_observer
.deleted());
2063 // Test that a pending RenderFrameHost in a non-root frame tree node is properly
2064 // deleted when the node is detached. Motivated by http://crbug.com/441357 and
2065 // http://crbug.com/444955.
2066 TEST_F(RenderFrameHostManagerTest
, DetachPendingChild
) {
2067 IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
2069 const GURL
kUrlA("http://www.google.com/");
2070 const GURL
kUrlB("http://webkit.org/");
2072 // Create a page with two child frames.
2073 contents()->NavigateAndCommit(kUrlA
);
2074 contents()->GetMainFrame()->OnCreateChildFrame(
2075 contents()->GetMainFrame()->GetProcess()->GetNextRoutingID(),
2076 blink::WebTreeScopeType::Document
, "frame_name",
2077 blink::WebSandboxFlags::None
);
2078 contents()->GetMainFrame()->OnCreateChildFrame(
2079 contents()->GetMainFrame()->GetProcess()->GetNextRoutingID(),
2080 blink::WebTreeScopeType::Document
, "frame_name",
2081 blink::WebSandboxFlags::None
);
2082 RenderFrameHostManager
* root_manager
=
2083 contents()->GetFrameTree()->root()->render_manager();
2084 RenderFrameHostManager
* iframe1
=
2085 contents()->GetFrameTree()->root()->child_at(0)->render_manager();
2086 RenderFrameHostManager
* iframe2
=
2087 contents()->GetFrameTree()->root()->child_at(1)->render_manager();
2089 // 1) The first navigation.
2090 NavigationEntryImpl
entryA(NULL
/* instance */, -1 /* page_id */, kUrlA
,
2091 Referrer(), base::string16() /* title */,
2092 ui::PAGE_TRANSITION_TYPED
,
2093 false /* is_renderer_init */);
2094 RenderFrameHostImpl
* host1
= NavigateToEntry(iframe1
, entryA
);
2096 // The RenderFrameHost created in Init will be reused.
2097 EXPECT_TRUE(host1
== iframe1
->current_frame_host());
2098 EXPECT_FALSE(GetPendingFrameHost(iframe1
));
2101 iframe1
->DidNavigateFrame(host1
, true);
2102 // Commit to SiteInstance should be delayed until RenderFrame commit.
2103 EXPECT_TRUE(host1
== iframe1
->current_frame_host());
2105 EXPECT_TRUE(host1
->GetSiteInstance()->HasSite());
2107 // 2) Cross-site navigate both frames to next site.
2108 NavigationEntryImpl
entryB(NULL
/* instance */, -1 /* page_id */, kUrlB
,
2109 Referrer(kUrlA
, blink::WebReferrerPolicyDefault
),
2110 base::string16() /* title */,
2111 ui::PAGE_TRANSITION_LINK
,
2112 false /* is_renderer_init */);
2113 host1
= NavigateToEntry(iframe1
, entryB
);
2114 RenderFrameHostImpl
* host2
= NavigateToEntry(iframe2
, entryB
);
2116 // A new, pending RenderFrameHost should be created in each FrameTreeNode.
2117 EXPECT_TRUE(GetPendingFrameHost(iframe1
));
2118 EXPECT_TRUE(GetPendingFrameHost(iframe2
));
2119 EXPECT_EQ(host1
, GetPendingFrameHost(iframe1
));
2120 EXPECT_EQ(host2
, GetPendingFrameHost(iframe2
));
2121 EXPECT_TRUE(RenderFrameHostImpl::IsRFHStateActive(
2122 GetPendingFrameHost(iframe1
)->rfh_state()));
2123 EXPECT_TRUE(RenderFrameHostImpl::IsRFHStateActive(
2124 GetPendingFrameHost(iframe2
)->rfh_state()));
2125 EXPECT_NE(GetPendingFrameHost(iframe1
), GetPendingFrameHost(iframe2
));
2126 EXPECT_EQ(GetPendingFrameHost(iframe1
)->GetSiteInstance(),
2127 GetPendingFrameHost(iframe2
)->GetSiteInstance());
2128 EXPECT_NE(iframe1
->current_frame_host(), GetPendingFrameHost(iframe1
));
2129 EXPECT_NE(iframe2
->current_frame_host(), GetPendingFrameHost(iframe2
));
2130 EXPECT_FALSE(contents()->CrossProcessNavigationPending())
2131 << "There should be no top-level pending navigation.";
2133 RenderFrameDeletedObserver
delete_watcher1(GetPendingFrameHost(iframe1
));
2134 RenderFrameDeletedObserver
delete_watcher2(GetPendingFrameHost(iframe2
));
2135 EXPECT_FALSE(delete_watcher1
.deleted());
2136 EXPECT_FALSE(delete_watcher2
.deleted());
2138 // Keep the SiteInstance alive for testing.
2139 scoped_refptr
<SiteInstanceImpl
> site_instance
=
2140 GetPendingFrameHost(iframe1
)->GetSiteInstance();
2141 EXPECT_TRUE(site_instance
->HasSite());
2142 EXPECT_NE(site_instance
, contents()->GetSiteInstance());
2143 EXPECT_EQ(2U, site_instance
->active_frame_count());
2145 // Proxies should exist.
2147 root_manager
->GetRenderFrameProxyHost(site_instance
.get()));
2149 iframe1
->GetRenderFrameProxyHost(site_instance
.get()));
2151 iframe2
->GetRenderFrameProxyHost(site_instance
.get()));
2153 // Detach the first child FrameTreeNode. This should kill the pending host but
2154 // not yet destroy proxies in |site_instance| since the other child remains.
2155 iframe1
->current_frame_host()->OnMessageReceived(
2156 FrameHostMsg_Detach(iframe1
->current_frame_host()->GetRoutingID()));
2157 iframe1
= NULL
; // Was just destroyed.
2159 EXPECT_TRUE(delete_watcher1
.deleted());
2160 EXPECT_FALSE(delete_watcher2
.deleted());
2161 EXPECT_EQ(1U, site_instance
->active_frame_count());
2163 // Proxies should still exist.
2165 root_manager
->GetRenderFrameProxyHost(site_instance
.get()));
2167 iframe2
->GetRenderFrameProxyHost(site_instance
.get()));
2169 // Detach the second child FrameTreeNode. This should trigger cleanup of
2170 // RenderFrameProxyHosts in |site_instance|.
2171 iframe2
->current_frame_host()->OnMessageReceived(
2172 FrameHostMsg_Detach(iframe2
->current_frame_host()->GetRoutingID()));
2173 iframe2
= NULL
; // Was just destroyed.
2175 EXPECT_TRUE(delete_watcher1
.deleted());
2176 EXPECT_TRUE(delete_watcher2
.deleted());
2178 EXPECT_EQ(0U, site_instance
->active_frame_count());
2180 root_manager
->GetRenderFrameProxyHost(site_instance
.get()))
2181 << "Proxies should have been cleaned up";
2182 EXPECT_TRUE(site_instance
->HasOneRef())
2183 << "This SiteInstance should be destroyable now.";
2186 // Two tabs in the same process crash. The first tab is reloaded, and the second
2187 // tab navigates away without reloading. The second tab's navigation shouldn't
2188 // mess with the first tab's content. Motivated by http://crbug.com/473714.
2189 TEST_F(RenderFrameHostManagerTest
, TwoTabsCrashOneReloadsOneLeaves
) {
2190 IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
2192 const GURL
kUrl1("http://www.google.com/");
2193 const GURL
kUrl2("http://webkit.org/");
2194 const GURL
kUrl3("http://whatwg.org/");
2196 // |contents1| and |contents2| navigate to the same page and then crash.
2197 TestWebContents
* contents1
= contents();
2198 scoped_ptr
<TestWebContents
> contents2(
2199 TestWebContents::Create(browser_context(), contents1
->GetSiteInstance()));
2200 contents1
->NavigateAndCommit(kUrl1
);
2201 contents2
->NavigateAndCommit(kUrl1
);
2202 MockRenderProcessHost
* rph
= contents1
->GetMainFrame()->GetProcess();
2203 EXPECT_EQ(rph
, contents2
->GetMainFrame()->GetProcess());
2204 rph
->SimulateCrash();
2205 EXPECT_FALSE(contents1
->GetMainFrame()->IsRenderFrameLive());
2206 EXPECT_FALSE(contents2
->GetMainFrame()->IsRenderFrameLive());
2207 EXPECT_EQ(contents1
->GetSiteInstance(), contents2
->GetSiteInstance());
2209 // Reload |contents1|.
2210 contents1
->NavigateAndCommit(kUrl1
);
2211 EXPECT_TRUE(contents1
->GetMainFrame()->IsRenderFrameLive());
2212 EXPECT_FALSE(contents2
->GetMainFrame()->IsRenderFrameLive());
2213 EXPECT_EQ(contents1
->GetSiteInstance(), contents2
->GetSiteInstance());
2215 // |contents1| creates an out of process iframe.
2216 contents1
->GetMainFrame()->OnCreateChildFrame(
2217 contents1
->GetMainFrame()->GetProcess()->GetNextRoutingID(),
2218 blink::WebTreeScopeType::Document
, "frame_name",
2219 blink::WebSandboxFlags::None
);
2220 RenderFrameHostManager
* iframe
=
2221 contents()->GetFrameTree()->root()->child_at(0)->render_manager();
2222 NavigationEntryImpl
entry(NULL
/* instance */, -1 /* page_id */, kUrl2
,
2223 Referrer(kUrl1
, blink::WebReferrerPolicyDefault
),
2224 base::string16() /* title */,
2225 ui::PAGE_TRANSITION_LINK
,
2226 false /* is_renderer_init */);
2227 RenderFrameHostImpl
* cross_site
= NavigateToEntry(iframe
, entry
);
2228 iframe
->DidNavigateFrame(cross_site
, true);
2230 // A proxy to the iframe should now exist in the SiteInstance of the main
2232 EXPECT_NE(cross_site
->GetSiteInstance(), contents1
->GetSiteInstance());
2234 iframe
->GetRenderFrameProxyHost(contents1
->GetSiteInstance()));
2236 iframe
->GetRenderFrameProxyHost(contents2
->GetSiteInstance()));
2238 // Navigate |contents2| away from the sad tab (and thus away from the
2239 // SiteInstance of |contents1|). This should not destroy the proxies needed by
2240 // |contents1| -- that was http://crbug.com/473714.
2241 EXPECT_FALSE(contents2
->GetMainFrame()->IsRenderFrameLive());
2242 contents2
->NavigateAndCommit(kUrl3
);
2243 EXPECT_TRUE(contents2
->GetMainFrame()->IsRenderFrameLive());
2245 iframe
->GetRenderFrameProxyHost(contents1
->GetSiteInstance()));
2247 iframe
->GetRenderFrameProxyHost(contents2
->GetSiteInstance()));
2250 // Test that opener proxies are created properly with a cycle on the opener
2252 TEST_F(RenderFrameHostManagerTest
, CreateOpenerProxiesWithCycleOnOpenerChain
) {
2253 const GURL
kUrl1("http://www.google.com/");
2254 const GURL
kUrl2("http://www.chromium.org/");
2256 // Navigate to an initial URL.
2257 contents()->NavigateAndCommit(kUrl1
);
2258 TestRenderFrameHost
* rfh1
= main_test_rfh();
2259 scoped_refptr
<SiteInstanceImpl
> site_instance1
= rfh1
->GetSiteInstance();
2261 // Create 2 new tabs and construct the opener chain as follows:
2263 // tab2 <--- tab1 <---- contents()
2267 scoped_ptr
<TestWebContents
> tab1(
2268 TestWebContents::Create(browser_context(), site_instance1
.get()));
2269 RenderFrameHostManager
* tab1_manager
= tab1
->GetRenderManagerForTesting();
2270 scoped_ptr
<TestWebContents
> tab2(
2271 TestWebContents::Create(browser_context(), site_instance1
.get()));
2272 RenderFrameHostManager
* tab2_manager
= tab2
->GetRenderManagerForTesting();
2274 contents()->SetOpener(tab1
.get());
2275 tab1
->SetOpener(tab2
.get());
2276 tab2
->SetOpener(tab1
.get());
2278 // Navigate main window to a cross-site URL. This will call
2279 // CreateOpenerProxies() to create proxies for the two opener tabs in the new
2281 contents()->NavigateAndCommit(kUrl2
);
2282 TestRenderFrameHost
* rfh2
= main_test_rfh();
2283 EXPECT_NE(site_instance1
, rfh2
->GetSiteInstance());
2285 // Check that each tab now has a proxy in the new SiteInstance.
2286 RenderFrameProxyHost
* tab1_proxy
=
2287 tab1_manager
->GetRenderFrameProxyHost(rfh2
->GetSiteInstance());
2288 EXPECT_TRUE(tab1_proxy
);
2289 RenderFrameProxyHost
* tab2_proxy
=
2290 tab2_manager
->GetRenderFrameProxyHost(rfh2
->GetSiteInstance());
2291 EXPECT_TRUE(tab2_proxy
);
2293 // Verify that the proxies' openers point to each other.
2294 int tab1_opener_routing_id
=
2295 tab1_manager
->GetOpenerRoutingID(rfh2
->GetSiteInstance());
2296 int tab2_opener_routing_id
=
2297 tab2_manager
->GetOpenerRoutingID(rfh2
->GetSiteInstance());
2298 EXPECT_EQ(tab2_proxy
->GetRoutingID(), tab1_opener_routing_id
);
2299 EXPECT_EQ(tab1_proxy
->GetRoutingID(), tab2_opener_routing_id
);
2301 // TODO(alexmos): Because of the cycle, tab2 will require a separate opener
2302 // update IPC. Verify that this IPC is sent once it's implemented.
2305 // Test that opener proxies are created properly when the opener points
2307 TEST_F(RenderFrameHostManagerTest
, CreateOpenerProxiesWhenOpenerPointsToSelf
) {
2308 const GURL
kUrl1("http://www.google.com/");
2309 const GURL
kUrl2("http://www.chromium.org/");
2311 // Navigate to an initial URL.
2312 contents()->NavigateAndCommit(kUrl1
);
2313 TestRenderFrameHost
* rfh1
= main_test_rfh();
2314 scoped_refptr
<SiteInstanceImpl
> site_instance1
= rfh1
->GetSiteInstance();
2316 // Create an opener tab, and simulate that its opener points to itself.
2317 scoped_ptr
<TestWebContents
> opener(
2318 TestWebContents::Create(browser_context(), site_instance1
.get()));
2319 RenderFrameHostManager
* opener_manager
= opener
->GetRenderManagerForTesting();
2320 contents()->SetOpener(opener
.get());
2321 opener
->SetOpener(opener
.get());
2323 // Navigate main window to a cross-site URL. This will call
2324 // CreateOpenerProxies() to create proxies for the opener tab in the new
2326 contents()->NavigateAndCommit(kUrl2
);
2327 TestRenderFrameHost
* rfh2
= main_test_rfh();
2328 EXPECT_NE(site_instance1
, rfh2
->GetSiteInstance());
2330 // Check that the opener now has a proxy in the new SiteInstance.
2331 RenderFrameProxyHost
* opener_proxy
=
2332 opener_manager
->GetRenderFrameProxyHost(rfh2
->GetSiteInstance());
2333 EXPECT_TRUE(opener_proxy
);
2335 // Verify that the proxy's opener points to itself.
2336 int opener_routing_id
=
2337 opener_manager
->GetOpenerRoutingID(rfh2
->GetSiteInstance());
2338 EXPECT_EQ(opener_proxy
->GetRoutingID(), opener_routing_id
);
2340 // TODO(alexmos): Because of the cycle, setting the opener in opener_proxy
2341 // will require a separate opener update IPC. Verify that this IPC is sent
2342 // once it's implemented.
2345 // Build the following frame opener graph and see that it can be properly
2346 // traversed when creating opener proxies:
2348 // +-> root4 <--+ root3 <---- root2 +--- root1
2349 // | / | ^ / \ | / \ .
2350 // | 42 +-----|------- 22 23 <--+ 12 13
2351 // | +------------+ | | ^
2352 // +-------------------------------+ +-+
2354 // The test starts traversing openers from root1 and expects to discover all
2355 // four FrameTrees. Nodes 13 (with cycle to itself) and 42 (with back link to
2356 // root3) should be put on the list of nodes that will need their frame openers
2357 // set separately in a second pass, since their opener routing IDs won't be
2358 // available during the first pass of CreateOpenerProxies.
2359 TEST_F(RenderFrameHostManagerTest
, TraverseComplexOpenerChain
) {
2360 FrameTree
* tree1
= contents()->GetFrameTree();
2361 FrameTreeNode
* root1
= tree1
->root();
2362 int process_id
= root1
->current_frame_host()->GetProcess()->GetID();
2363 tree1
->AddFrame(root1
, process_id
, 12, blink::WebTreeScopeType::Document
,
2364 std::string(), blink::WebSandboxFlags::None
);
2365 tree1
->AddFrame(root1
, process_id
, 13, blink::WebTreeScopeType::Document
,
2366 std::string(), blink::WebSandboxFlags::None
);
2368 scoped_ptr
<TestWebContents
> tab2(
2369 TestWebContents::Create(browser_context(), nullptr));
2370 FrameTree
* tree2
= tab2
->GetFrameTree();
2371 FrameTreeNode
* root2
= tree2
->root();
2372 process_id
= root2
->current_frame_host()->GetProcess()->GetID();
2373 tree2
->AddFrame(root2
, process_id
, 22, blink::WebTreeScopeType::Document
,
2374 std::string(), blink::WebSandboxFlags::None
);
2375 tree2
->AddFrame(root2
, process_id
, 23, blink::WebTreeScopeType::Document
,
2376 std::string(), blink::WebSandboxFlags::None
);
2378 scoped_ptr
<TestWebContents
> tab3(
2379 TestWebContents::Create(browser_context(), nullptr));
2380 FrameTree
* tree3
= tab3
->GetFrameTree();
2381 FrameTreeNode
* root3
= tree3
->root();
2383 scoped_ptr
<TestWebContents
> tab4(
2384 TestWebContents::Create(browser_context(), nullptr));
2385 FrameTree
* tree4
= tab4
->GetFrameTree();
2386 FrameTreeNode
* root4
= tree4
->root();
2387 process_id
= root4
->current_frame_host()->GetProcess()->GetID();
2388 tree4
->AddFrame(root4
, process_id
, 42, blink::WebTreeScopeType::Document
,
2389 std::string(), blink::WebSandboxFlags::None
);
2391 root1
->child_at(1)->SetOpener(root1
->child_at(1));
2392 root1
->SetOpener(root2
->child_at(1));
2393 root2
->SetOpener(root3
);
2394 root2
->child_at(0)->SetOpener(root4
);
2395 root2
->child_at(1)->SetOpener(root4
);
2396 root4
->child_at(0)->SetOpener(root3
);
2398 std::vector
<FrameTree
*> opener_frame_trees
;
2399 base::hash_set
<FrameTreeNode
*> nodes_with_back_links
;
2401 CollectOpenerFrameTrees(root1
, &opener_frame_trees
, &nodes_with_back_links
);
2403 EXPECT_EQ(4U, opener_frame_trees
.size());
2404 EXPECT_EQ(tree1
, opener_frame_trees
[0]);
2405 EXPECT_EQ(tree2
, opener_frame_trees
[1]);
2406 EXPECT_EQ(tree3
, opener_frame_trees
[2]);
2407 EXPECT_EQ(tree4
, opener_frame_trees
[3]);
2409 EXPECT_EQ(2U, nodes_with_back_links
.size());
2410 EXPECT_TRUE(nodes_with_back_links
.find(root1
->child_at(1)) !=
2411 nodes_with_back_links
.end());
2412 EXPECT_TRUE(nodes_with_back_links
.find(root4
->child_at(0)) !=
2413 nodes_with_back_links
.end());
2416 } // namespace content