Make USB permissions work in the new permission message system
[chromium-blink-merge.git] / content / browser / frame_host / render_frame_host_manager_unittest.cc
blobb5ab37e1645fe20092fc5d99f6490a699cda422a
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"
50 namespace content {
51 namespace {
53 class RenderFrameHostManagerTestWebUIControllerFactory
54 : public WebUIControllerFactory {
55 public:
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)))
69 return NULL;
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);
88 private:
89 bool should_create_webui_;
91 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostManagerTestWebUIControllerFactory);
94 class BeforeUnloadFiredWebContentsDelegate : public WebContentsDelegate {
95 public:
96 BeforeUnloadFiredWebContentsDelegate() {}
97 ~BeforeUnloadFiredWebContentsDelegate() override {}
99 void BeforeUnloadFired(WebContents* web_contents,
100 bool proceed,
101 bool* proceed_to_fire_unload) override {
102 *proceed_to_fire_unload = proceed;
105 private:
106 DISALLOW_COPY_AND_ASSIGN(BeforeUnloadFiredWebContentsDelegate);
109 class CloseWebContentsDelegate : public WebContentsDelegate {
110 public:
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_; }
120 private:
121 DISALLOW_COPY_AND_ASSIGN(CloseWebContentsDelegate);
123 bool close_called_;
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 {
129 public:
130 RenderViewHostDeletedObserver(RenderViewHost* rvh)
131 : WebContentsObserver(WebContents::FromRenderViewHost(rvh)),
132 process_id_(rvh->GetProcess()->GetID()),
133 routing_id_(rvh->GetRoutingID()),
134 deleted_(false) {
137 void RenderViewDeleted(RenderViewHost* render_view_host) override {
138 if (render_view_host->GetProcess()->GetID() == process_id_ &&
139 render_view_host->GetRoutingID() == routing_id_) {
140 deleted_ = true;
144 bool deleted() {
145 return deleted_;
148 private:
149 int process_id_;
150 int routing_id_;
151 bool deleted_;
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 {
159 public:
160 RenderFrameHostCreatedObserver(WebContents* web_contents)
161 : WebContentsObserver(web_contents),
162 created_(false) {
165 void RenderFrameCreated(RenderFrameHost* render_frame_host) override {
166 created_ = true;
169 bool created() {
170 return created_;
173 private:
174 bool created_;
176 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostCreatedObserver);
179 // This WebContents observer keep track of its RVH change.
180 class RenderViewHostChangedObserver : public WebContentsObserver {
181 public:
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_;
193 Reset();
194 return host_changed;
197 void Reset() { host_changed_ = false; }
199 private:
200 bool host_changed_;
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 {
210 public:
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_;
233 private:
234 bool plugin_crashed_;
235 bool favicon_received_;
237 DISALLOW_COPY_AND_ASSIGN(PluginFaviconMessageObserver);
240 } // namespace
242 class RenderFrameHostManagerTest : public RenderViewHostImplTestHarness {
243 public:
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));
250 #endif
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();
260 #endif
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
270 // for us.
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()
289 : old_rfh;
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;
310 EXPECT_TRUE(
311 old_rfh->frame_tree_node()->render_manager()->IsPendingDeletion(
312 old_rfh));
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());
325 } else {
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 {
337 CHECK(new_entry);
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();
376 CHECK(dest_rfh);
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());
386 return ntp_rfh;
389 // Returns the RenderFrameHost that should be used in the navigation to
390 // |entry|.
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));
406 CHECK(frame_host);
407 frame_host->set_pending_commit(true);
408 return frame_host;
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(
427 FrameTreeNode* node,
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);
434 private:
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,
499 kChromeUrl);
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
532 // site.
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());
544 EXPECT_TRUE(
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());
555 EXPECT_TRUE(
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()) {
565 return;
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());
579 #endif
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");
588 bool result = false;
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(),
649 icons)));
650 EXPECT_TRUE(observer.favicon_received());
653 // The old renderer, being slow, now updates its favicons. The message should
654 // be ignored.
656 PluginFaviconMessageObserver observer(contents());
657 EXPECT_TRUE(rfh1->GetRenderViewHost()->OnMessageReceived(
658 ViewHostMsg_UpdateFaviconURL(rfh1->GetRenderViewHost()->GetRoutingID(),
659 icons)));
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
673 // longer used.
674 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
675 return;
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
692 // site.
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()) {
719 return;
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
739 // widgets.
740 TEST_F(RenderFrameHostManagerTest, GetRenderWidgetHostsReturnsActiveViews) {
741 // This test is invalid in --site-per-process mode, as swapped-out is no
742 // longer used.
743 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
744 return;
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
754 // list.
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
769 // longer used.
770 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
771 return;
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()) {
781 bool found = false;
782 scoped_ptr<RenderWidgetHostIterator> all_widgets(
783 RenderWidgetHostImpl::GetAllRenderWidgetHosts());
784 while (RenderWidgetHost* widget = all_widgets->GetNextHost()) {
785 if (w == widget) {
786 found = true;
787 break;
790 EXPECT_TRUE(found);
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 {
844 public:
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_;
856 private:
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(),
884 web_contents);
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
892 // all widgets.
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
910 // target code path.
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
916 // GetURL() call.
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();
926 int32 new_id =
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();
943 // Navigate, again.
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();
977 ASSERT_TRUE(rvh);
978 ASSERT_TRUE(rfh);
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));
1011 // Commit.
1012 manager->DidNavigateFrame(host, true);
1013 // Commit to SiteInstance should be delayed until RenderFrame commit.
1014 EXPECT_TRUE(host == manager->current_frame_host());
1015 ASSERT_TRUE(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));
1032 // Commit.
1033 manager->DidNavigateFrame(host, true);
1034 EXPECT_TRUE(host == manager->current_frame_host());
1035 ASSERT_TRUE(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();
1053 // Commit.
1054 manager->DidNavigateFrame(GetPendingFrameHost(manager), true);
1055 EXPECT_TRUE(host == manager->current_frame_host());
1056 ASSERT_TRUE(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
1088 // WebUIInNewTab.
1089 EXPECT_TRUE(host);
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());
1106 } else {
1107 EXPECT_FALSE(manager->pending_web_ui());
1109 EXPECT_TRUE(manager->web_ui());
1111 // Commit.
1112 manager->DidNavigateFrame(host, true);
1113 EXPECT_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());
1147 EXPECT_TRUE(
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());
1154 EXPECT_TRUE(
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());
1181 } else {
1182 EXPECT_TRUE(manager2->pending_web_ui());
1184 EXPECT_TRUE(
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
1203 // current WebUI.
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";
1243 return;
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
1251 // deleted.
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;
1268 params.page_id = 0;
1269 params.nav_entry_id = 0;
1270 params.did_create_new_entry = false;
1271 params.url = kUrl2;
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(&params);
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() !=
1289 NULL);
1290 EXPECT_EQ(evil_rfh,
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(&params);
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() ==
1311 NULL);
1312 EXPECT_EQ(evil_rfh,
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));
1416 EXPECT_EQ(rfh1,
1417 manager->GetRenderFrameProxyHost(site_instance1.get())
1418 ->render_frame_host());
1419 } else {
1420 EXPECT_TRUE(rfh1_deleted_observer.deleted());
1421 EXPECT_TRUE(manager->GetRenderFrameProxyHost(site_instance1.get()));
1423 EXPECT_EQ(rvh1,
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());
1436 } else {
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());
1451 } else {
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
1525 // in progress.
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
1567 // commits.
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.
1625 EXPECT_FALSE(
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
1638 // is deleted.
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.
1645 EXPECT_TRUE(
1646 opener1_manager->GetSwappedOutRenderViewHost(rfh1->GetSiteInstance()));
1647 EXPECT_FALSE(
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();
1655 EXPECT_TRUE(
1656 opener1_manager->GetSwappedOutRenderViewHost(rfh1->GetSiteInstance())
1657 ->IsRenderViewLive());
1658 EXPECT_EQ(
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());
1704 } else {
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);
1738 // Commit.
1739 manager->DidNavigateFrame(host, true);
1740 // Commit to SiteInstance should be delayed until RenderFrame commit.
1741 EXPECT_EQ(host, manager->current_frame_host());
1742 ASSERT_TRUE(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());
1759 // Commit.
1760 manager->DidNavigateFrame(host, true);
1761 EXPECT_EQ(host, manager->current_frame_host());
1762 ASSERT_TRUE(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());
1796 // Commit.
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());
1825 EXPECT_TRUE(
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
1883 // have no effect.
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());
1896 EXPECT_TRUE(
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());
1904 rfh1 = NULL;
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());
1948 } else {
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()));
1998 } else {
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
2005 // is cancelled.
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
2031 // be deleted.
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
2044 // created.
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()));
2057 } else {
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));
2100 // Commit.
2101 iframe1->DidNavigateFrame(host1, true);
2102 // Commit to SiteInstance should be delayed until RenderFrame commit.
2103 EXPECT_TRUE(host1 == iframe1->current_frame_host());
2104 ASSERT_TRUE(host1);
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.
2146 EXPECT_NE(nullptr,
2147 root_manager->GetRenderFrameProxyHost(site_instance.get()));
2148 EXPECT_NE(nullptr,
2149 iframe1->GetRenderFrameProxyHost(site_instance.get()));
2150 EXPECT_NE(nullptr,
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.
2164 EXPECT_NE(nullptr,
2165 root_manager->GetRenderFrameProxyHost(site_instance.get()));
2166 EXPECT_NE(nullptr,
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());
2179 EXPECT_EQ(nullptr,
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
2231 // frames.
2232 EXPECT_NE(cross_site->GetSiteInstance(), contents1->GetSiteInstance());
2233 EXPECT_NE(nullptr,
2234 iframe->GetRenderFrameProxyHost(contents1->GetSiteInstance()));
2235 EXPECT_NE(nullptr,
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());
2244 EXPECT_NE(nullptr,
2245 iframe->GetRenderFrameProxyHost(contents1->GetSiteInstance()));
2246 EXPECT_EQ(nullptr,
2247 iframe->GetRenderFrameProxyHost(contents2->GetSiteInstance()));
2250 // Test that opener proxies are created properly with a cycle on the opener
2251 // chain.
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()
2264 // | ^
2265 // +-------+
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
2280 // SiteInstance.
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
2306 // to itself.
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
2325 // SiteInstance.
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