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