Fix various typos, error handling for GN auto-roller.
[chromium-blink-merge.git] / content / browser / frame_host / render_frame_host_manager_unittest.cc
blob193ca583479ec2b894cd55567cede1b7053c7818
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/browser_test_utils.h"
38 #include "content/public/test/mock_render_process_host.h"
39 #include "content/public/test/test_notification_tracker.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 #if !defined(OS_ANDROID)
255 ImageTransportFactory::Terminate();
256 #endif
257 RenderViewHostImplTestHarness::TearDown();
258 WebUIControllerFactory::UnregisterFactoryForTesting(&factory_);
261 void set_should_create_webui(bool should_create_webui) {
262 factory_.set_should_create_webui(should_create_webui);
265 void NavigateActiveAndCommit(const GURL& url) {
266 // Note: we navigate the active RenderFrameHost because previous navigations
267 // won't have committed yet, so NavigateAndCommit does the wrong thing
268 // for us.
269 controller().LoadURL(
270 url, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
271 int entry_id = controller().GetPendingEntry()->GetUniqueID();
273 // Simulate the BeforeUnload_ACK that is received from the current renderer
274 // for a cross-site navigation.
275 // PlzNavigate: it is necessary to call PrepareForCommit before getting the
276 // main and the pending frame because when we are trying to navigate to a
277 // WebUI from a new tab, a RenderFrameHost is created to display it that is
278 // committed immediately (since it is a new tab). Therefore the main frame
279 // is replaced without a pending frame being created, and we don't get the
280 // right values for the RFH to navigate: we try to use the old one that has
281 // been deleted in the meantime.
282 contents()->GetMainFrame()->PrepareForCommit();
284 TestRenderFrameHost* old_rfh = contents()->GetMainFrame();
285 TestRenderFrameHost* active_rfh = contents()->GetPendingMainFrame()
286 ? contents()->GetPendingMainFrame()
287 : old_rfh;
288 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, old_rfh->rfh_state());
290 // Commit the navigation with a new page ID.
291 int32 max_page_id = contents()->GetMaxPageIDForSiteInstance(
292 active_rfh->GetSiteInstance());
294 // Use an observer to avoid accessing a deleted renderer later on when the
295 // state is being checked.
296 RenderFrameDeletedObserver rfh_observer(old_rfh);
297 RenderViewHostDeletedObserver rvh_observer(old_rfh->GetRenderViewHost());
298 active_rfh->SendNavigate(max_page_id + 1, entry_id, true, url);
300 // Make sure that we start to run the unload handler at the time of commit.
301 bool expecting_rfh_shutdown = false;
302 if (old_rfh != active_rfh && !rfh_observer.deleted()) {
303 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT,
304 old_rfh->rfh_state());
305 if (!old_rfh->GetSiteInstance()->active_frame_count() ||
306 RenderFrameHostManager::IsSwappedOutStateForbidden()) {
307 expecting_rfh_shutdown = true;
308 EXPECT_TRUE(
309 old_rfh->frame_tree_node()->render_manager()->IsPendingDeletion(
310 old_rfh));
314 // Simulate the swap out ACK coming from the pending renderer. This should
315 // either shut down the old RFH or leave it in a swapped out state.
316 if (old_rfh != active_rfh) {
317 old_rfh->OnSwappedOut();
318 if (expecting_rfh_shutdown) {
319 EXPECT_TRUE(rfh_observer.deleted());
320 if (!RenderFrameHostManager::IsSwappedOutStateForbidden()) {
321 EXPECT_TRUE(rvh_observer.deleted());
323 } else {
324 EXPECT_EQ(RenderFrameHostImpl::STATE_SWAPPED_OUT,
325 old_rfh->rfh_state());
328 EXPECT_EQ(active_rfh, contents()->GetMainFrame());
329 EXPECT_EQ(NULL, contents()->GetPendingMainFrame());
332 bool ShouldSwapProcesses(RenderFrameHostManager* manager,
333 const NavigationEntryImpl* current_entry,
334 const NavigationEntryImpl* new_entry) const {
335 CHECK(new_entry);
336 BrowserContext* browser_context =
337 manager->delegate_->GetControllerForRenderManager().GetBrowserContext();
338 const GURL& current_effective_url = current_entry ?
339 SiteInstanceImpl::GetEffectiveURL(browser_context,
340 current_entry->GetURL()) :
341 manager->render_frame_host_->GetSiteInstance()->GetSiteURL();
342 bool current_is_view_source_mode = current_entry ?
343 current_entry->IsViewSourceMode() : new_entry->IsViewSourceMode();
344 return manager->ShouldSwapBrowsingInstancesForNavigation(
345 current_effective_url,
346 current_is_view_source_mode,
347 new_entry->site_instance(),
348 SiteInstanceImpl::GetEffectiveURL(browser_context, new_entry->GetURL()),
349 new_entry->IsViewSourceMode());
352 // Creates a test RenderFrameHost that's swapped out.
353 TestRenderFrameHost* CreateSwappedOutRenderFrameHost() {
354 const GURL kChromeURL("chrome://foo");
355 const GURL kDestUrl("http://www.google.com/");
357 // Navigate our first tab to a chrome url and then to the destination.
358 NavigateActiveAndCommit(kChromeURL);
359 TestRenderFrameHost* ntp_rfh = contents()->GetMainFrame();
361 // Navigate to a cross-site URL.
362 contents()->GetController().LoadURL(
363 kDestUrl, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
364 int entry_id = contents()->GetController().GetPendingEntry()->GetUniqueID();
365 contents()->GetMainFrame()->PrepareForCommit();
366 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
368 // Manually increase the number of active frames in the
369 // SiteInstance that ntp_rfh belongs to, to prevent it from being
370 // destroyed when it gets swapped out.
371 ntp_rfh->GetSiteInstance()->increment_active_frame_count();
373 TestRenderFrameHost* dest_rfh = contents()->GetPendingMainFrame();
374 CHECK(dest_rfh);
375 EXPECT_NE(ntp_rfh, dest_rfh);
377 // BeforeUnload finishes.
378 ntp_rfh->SendBeforeUnloadACK(true);
380 dest_rfh->SendNavigate(101, entry_id, true, kDestUrl);
381 ntp_rfh->OnSwappedOut();
383 EXPECT_TRUE(ntp_rfh->is_swapped_out());
384 return ntp_rfh;
387 // Returns the RenderFrameHost that should be used in the navigation to
388 // |entry|.
389 RenderFrameHostImpl* NavigateToEntry(
390 RenderFrameHostManager* manager,
391 const NavigationEntryImpl& entry) {
392 // Tests currently only navigate using main frame FrameNavigationEntries.
393 FrameNavigationEntry* frame_entry = entry.root_node()->frame_entry.get();
394 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
395 switches::kEnableBrowserSideNavigation)) {
396 scoped_ptr<NavigationRequest> navigation_request =
397 NavigationRequest::CreateBrowserInitiated(
398 manager->frame_tree_node_, frame_entry->url(),
399 frame_entry->referrer(), *frame_entry, entry,
400 FrameMsg_Navigate_Type::NORMAL, false, base::TimeTicks::Now(),
401 static_cast<NavigationControllerImpl*>(&controller()));
402 TestRenderFrameHost* frame_host = static_cast<TestRenderFrameHost*>(
403 manager->GetFrameHostForNavigation(*navigation_request));
404 CHECK(frame_host);
405 frame_host->set_pending_commit(true);
406 return frame_host;
409 return manager->Navigate(frame_entry->url(), *frame_entry, entry);
412 // Returns the pending RenderFrameHost.
413 // PlzNavigate: returns the speculative RenderFrameHost.
414 RenderFrameHostImpl* GetPendingFrameHost(
415 RenderFrameHostManager* manager) {
416 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
417 switches::kEnableBrowserSideNavigation)) {
418 return manager->speculative_render_frame_host_.get();
420 return manager->pending_frame_host();
423 private:
424 RenderFrameHostManagerTestWebUIControllerFactory factory_;
427 // Tests that when you navigate from a chrome:// url to another page, and
428 // then do that same thing in another tab, that the two resulting pages have
429 // different SiteInstances, BrowsingInstances, and RenderProcessHosts. This is
430 // a regression test for bug 9364.
431 TEST_F(RenderFrameHostManagerTest, NewTabPageProcesses) {
432 set_should_create_webui(true);
433 const GURL kChromeUrl("chrome://foo");
434 const GURL kDestUrl("http://www.google.com/");
436 // Navigate our first tab to the chrome url and then to the destination,
437 // ensuring we grant bindings to the chrome URL.
438 NavigateActiveAndCommit(kChromeUrl);
439 EXPECT_TRUE(active_rvh()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
440 NavigateActiveAndCommit(kDestUrl);
442 EXPECT_FALSE(contents()->GetPendingMainFrame());
444 // Make a second tab.
445 scoped_ptr<TestWebContents> contents2(
446 TestWebContents::Create(browser_context(), NULL));
448 // Load the two URLs in the second tab. Note that the first navigation creates
449 // a RFH that's not pending (since there is no cross-site transition), so
450 // we use the committed one.
451 contents2->GetController().LoadURL(
452 kChromeUrl, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
453 int entry_id = contents2->GetController().GetPendingEntry()->GetUniqueID();
454 contents2->GetMainFrame()->PrepareForCommit();
455 TestRenderFrameHost* ntp_rfh2 = contents2->GetMainFrame();
456 EXPECT_FALSE(contents2->CrossProcessNavigationPending());
457 ntp_rfh2->SendNavigate(100, entry_id, true, kChromeUrl);
459 // The second one is the opposite, creating a cross-site transition and
460 // requiring a beforeunload ack.
461 contents2->GetController().LoadURL(
462 kDestUrl, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
463 entry_id = contents2->GetController().GetPendingEntry()->GetUniqueID();
464 contents2->GetMainFrame()->PrepareForCommit();
465 EXPECT_TRUE(contents2->CrossProcessNavigationPending());
466 TestRenderFrameHost* dest_rfh2 = contents2->GetPendingMainFrame();
467 ASSERT_TRUE(dest_rfh2);
469 dest_rfh2->SendNavigate(101, entry_id, true, kDestUrl);
471 // The two RFH's should be different in every way.
472 EXPECT_NE(contents()->GetMainFrame()->GetProcess(), dest_rfh2->GetProcess());
473 EXPECT_NE(contents()->GetMainFrame()->GetSiteInstance(),
474 dest_rfh2->GetSiteInstance());
475 EXPECT_FALSE(dest_rfh2->GetSiteInstance()->IsRelatedSiteInstance(
476 contents()->GetMainFrame()->GetSiteInstance()));
478 // Navigate both to the new tab page, and verify that they share a
479 // RenderProcessHost (not a SiteInstance).
480 NavigateActiveAndCommit(kChromeUrl);
481 EXPECT_FALSE(contents()->GetPendingMainFrame());
483 contents2->GetController().LoadURL(
484 kChromeUrl, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
485 entry_id = contents2->GetController().GetPendingEntry()->GetUniqueID();
486 contents2->GetMainFrame()->PrepareForCommit();
487 contents2->GetPendingMainFrame()->SendNavigate(102, entry_id, true,
488 kChromeUrl);
490 EXPECT_NE(contents()->GetMainFrame()->GetSiteInstance(),
491 contents2->GetMainFrame()->GetSiteInstance());
492 EXPECT_EQ(contents()->GetMainFrame()->GetSiteInstance()->GetProcess(),
493 contents2->GetMainFrame()->GetSiteInstance()->GetProcess());
496 // Ensure that the browser ignores most IPC messages that arrive from a
497 // RenderViewHost that has been swapped out. We do not want to take
498 // action on requests from a non-active renderer. The main exception is
499 // for synchronous messages, which cannot be ignored without leaving the
500 // renderer in a stuck state. See http://crbug.com/93427.
501 TEST_F(RenderFrameHostManagerTest, FilterMessagesWhileSwappedOut) {
502 const GURL kChromeURL("chrome://foo");
503 const GURL kDestUrl("http://www.google.com/");
504 std::vector<FaviconURL> icons;
506 // Navigate our first tab to a chrome url and then to the destination.
507 NavigateActiveAndCommit(kChromeURL);
508 TestRenderFrameHost* ntp_rfh = contents()->GetMainFrame();
509 TestRenderViewHost* ntp_rvh = ntp_rfh->GetRenderViewHost();
511 // Send an update favicon message and make sure it works.
513 PluginFaviconMessageObserver observer(contents());
514 EXPECT_TRUE(ntp_rfh->GetRenderViewHost()->OnMessageReceived(
515 ViewHostMsg_UpdateFaviconURL(
516 ntp_rfh->GetRenderViewHost()->GetRoutingID(), icons)));
517 EXPECT_TRUE(observer.favicon_received());
519 // Create one more frame in the same SiteInstance where ntp_rfh
520 // exists so that it doesn't get deleted on navigation to another
521 // site.
522 ntp_rfh->GetSiteInstance()->increment_active_frame_count();
524 // Navigate to a cross-site URL.
525 NavigateActiveAndCommit(kDestUrl);
526 TestRenderFrameHost* dest_rfh = contents()->GetMainFrame();
527 ASSERT_TRUE(dest_rfh);
528 EXPECT_NE(ntp_rfh, dest_rfh);
530 // The new RVH should be able to update its favicon.
532 PluginFaviconMessageObserver observer(contents());
533 EXPECT_TRUE(
534 dest_rfh->GetRenderViewHost()->OnMessageReceived(
535 ViewHostMsg_UpdateFaviconURL(
536 dest_rfh->GetRenderViewHost()->GetRoutingID(), icons)));
537 EXPECT_TRUE(observer.favicon_received());
540 // The old renderer, being slow, now updates the favicon. It should be
541 // filtered out and not take effect.
543 PluginFaviconMessageObserver observer(contents());
544 EXPECT_TRUE(
545 ntp_rvh->OnMessageReceived(
546 ViewHostMsg_UpdateFaviconURL(
547 dest_rfh->GetRenderViewHost()->GetRoutingID(), icons)));
548 EXPECT_FALSE(observer.favicon_received());
551 // In --site-per-process, the RenderFrameHost is deleted on cross-process
552 // navigation, so the rest of the test case doesn't apply.
553 if (RenderFrameHostManager::IsSwappedOutStateForbidden()) {
554 return;
557 #if defined(ENABLE_PLUGINS)
558 // The same logic should apply to RenderFrameHosts as well and routing through
559 // swapped out RFH shouldn't be allowed. Use a PluginCrashObserver to check
560 // if the IPC message is allowed through or not.
562 PluginFaviconMessageObserver observer(contents());
563 EXPECT_TRUE(ntp_rfh->OnMessageReceived(
564 FrameHostMsg_PluginCrashed(
565 ntp_rfh->GetRoutingID(), base::FilePath(), 0)));
566 EXPECT_FALSE(observer.plugin_crashed());
568 #endif
570 // We cannot filter out synchronous IPC messages, because the renderer would
571 // be left waiting for a reply. We pick RunBeforeUnloadConfirm as an example
572 // that can run easily within a unit test, and that needs to receive a reply
573 // without showing an actual dialog.
574 MockRenderProcessHost* ntp_process_host = ntp_rfh->GetProcess();
575 ntp_process_host->sink().ClearMessages();
576 const base::string16 msg = base::ASCIIToUTF16("Message");
577 bool result = false;
578 base::string16 unused;
579 FrameHostMsg_RunBeforeUnloadConfirm before_unload_msg(
580 ntp_rfh->GetRoutingID(), kChromeURL, msg, false, &result, &unused);
581 // Enable pumping for check in BrowserMessageFilter::CheckCanDispatchOnUI.
582 before_unload_msg.EnableMessagePumping();
583 EXPECT_TRUE(ntp_rfh->OnMessageReceived(before_unload_msg));
584 EXPECT_TRUE(ntp_process_host->sink().GetUniqueMessageMatching(IPC_REPLY_ID));
586 // Also test RunJavaScriptMessage.
587 ntp_process_host->sink().ClearMessages();
588 FrameHostMsg_RunJavaScriptMessage js_msg(
589 ntp_rfh->GetRoutingID(), msg, msg, kChromeURL,
590 JAVASCRIPT_MESSAGE_TYPE_CONFIRM, &result, &unused);
591 js_msg.EnableMessagePumping();
592 EXPECT_TRUE(ntp_rfh->OnMessageReceived(js_msg));
593 EXPECT_TRUE(ntp_process_host->sink().GetUniqueMessageMatching(IPC_REPLY_ID));
596 // Test that the ViewHostMsg_UpdateFaviconURL IPC message is ignored if the
597 // renderer is in the STATE_PENDING_SWAP_OUT_STATE. The favicon code assumes
598 // that it only gets ViewHostMsg_UpdateFaviconURL messages for the most recently
599 // committed navigation for each WebContentsImpl.
600 TEST_F(RenderFrameHostManagerTest, UpdateFaviconURLWhilePendingSwapOut) {
601 const GURL kChromeURL("chrome://foo");
602 const GURL kDestUrl("http://www.google.com/");
603 std::vector<FaviconURL> icons;
605 // Navigate our first tab to a chrome url and then to the destination.
606 NavigateActiveAndCommit(kChromeURL);
607 TestRenderFrameHost* rfh1 = contents()->GetMainFrame();
609 // Send an update favicon message and make sure it works.
611 PluginFaviconMessageObserver observer(contents());
612 EXPECT_TRUE(rfh1->GetRenderViewHost()->OnMessageReceived(
613 ViewHostMsg_UpdateFaviconURL(
614 rfh1->GetRenderViewHost()->GetRoutingID(), icons)));
615 EXPECT_TRUE(observer.favicon_received());
618 // Create one more frame in the same SiteInstance where |rfh1| exists so that
619 // it doesn't get deleted on navigation to another site.
620 rfh1->GetSiteInstance()->increment_active_frame_count();
622 // Navigate to a cross-site URL and commit the new page.
623 controller().LoadURL(
624 kDestUrl, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
625 int entry_id = controller().GetPendingEntry()->GetUniqueID();
626 contents()->GetMainFrame()->PrepareForCommit();
627 TestRenderFrameHost* rfh2 = contents()->GetPendingMainFrame();
628 contents()->TestDidNavigate(rfh2, 1, entry_id, true, kDestUrl,
629 ui::PAGE_TRANSITION_TYPED);
630 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh2->rfh_state());
631 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT, rfh1->rfh_state());
633 // The new RVH should be able to update its favicons.
635 PluginFaviconMessageObserver observer(contents());
636 EXPECT_TRUE(rfh2->GetRenderViewHost()->OnMessageReceived(
637 ViewHostMsg_UpdateFaviconURL(rfh2->GetRenderViewHost()->GetRoutingID(),
638 icons)));
639 EXPECT_TRUE(observer.favicon_received());
642 // The old renderer, being slow, now updates its favicons. The message should
643 // be ignored.
645 PluginFaviconMessageObserver observer(contents());
646 EXPECT_TRUE(rfh1->GetRenderViewHost()->OnMessageReceived(
647 ViewHostMsg_UpdateFaviconURL(rfh1->GetRenderViewHost()->GetRoutingID(),
648 icons)));
649 EXPECT_FALSE(observer.favicon_received());
653 // Ensure that frames aren't added to the frame tree, if the message is coming
654 // from a process different than the parent frame's current RenderFrameHost
655 // process. Otherwise it is possible to have collisions of routing ids, as they
656 // are scoped per process. See https://crbug.com/415059.
657 TEST_F(RenderFrameHostManagerTest, DropCreateChildFrameWhileSwappedOut) {
658 const GURL kUrl1("http://foo.com");
659 const GURL kUrl2("http://www.google.com/");
661 // This test is invalid in --site-per-process mode, as swapped-out is no
662 // longer used.
663 if (RenderFrameHostManager::IsSwappedOutStateForbidden()) {
664 return;
667 // Navigate to the first site.
668 NavigateActiveAndCommit(kUrl1);
669 TestRenderFrameHost* initial_rfh = contents()->GetMainFrame();
671 RenderFrameHostCreatedObserver observer(contents());
672 initial_rfh->OnCreateChildFrame(
673 initial_rfh->GetProcess()->GetNextRoutingID(),
674 blink::WebTreeScopeType::Document, std::string(),
675 blink::WebSandboxFlags::None);
676 EXPECT_TRUE(observer.created());
679 // Create one more frame in the same SiteInstance where initial_rfh
680 // exists so that initial_rfh doesn't get deleted on navigation to another
681 // site.
682 initial_rfh->GetSiteInstance()->increment_active_frame_count();
684 // Navigate to a cross-site URL.
685 NavigateActiveAndCommit(kUrl2);
686 EXPECT_TRUE(initial_rfh->is_swapped_out());
688 TestRenderFrameHost* dest_rfh = contents()->GetMainFrame();
689 ASSERT_TRUE(dest_rfh);
690 EXPECT_NE(initial_rfh, dest_rfh);
693 // Since the old RFH is now swapped out, it shouldn't process any messages
694 // to create child frames.
695 RenderFrameHostCreatedObserver observer(contents());
696 initial_rfh->OnCreateChildFrame(
697 initial_rfh->GetProcess()->GetNextRoutingID(),
698 blink::WebTreeScopeType::Document, std::string(),
699 blink::WebSandboxFlags::None);
700 EXPECT_FALSE(observer.created());
704 TEST_F(RenderFrameHostManagerTest, WhiteListSwapCompositorFrame) {
705 // TODO(nasko): Check with kenrb whether this test can be rewritten and
706 // whether it makes sense when swapped out is replaced with proxies.
707 if (RenderFrameHostManager::IsSwappedOutStateForbidden()) {
708 return;
710 TestRenderFrameHost* swapped_out_rfh = CreateSwappedOutRenderFrameHost();
711 TestRenderWidgetHostView* swapped_out_rwhv =
712 static_cast<TestRenderWidgetHostView*>(
713 swapped_out_rfh->GetRenderViewHost()->GetView());
714 EXPECT_FALSE(swapped_out_rwhv->did_swap_compositor_frame());
716 MockRenderProcessHost* process_host = swapped_out_rfh->GetProcess();
717 process_host->sink().ClearMessages();
719 cc::CompositorFrame frame;
720 ViewHostMsg_SwapCompositorFrame msg(
721 rvh()->GetRoutingID(), 0, frame, std::vector<IPC::Message>());
723 EXPECT_TRUE(swapped_out_rfh->render_view_host()->OnMessageReceived(msg));
724 EXPECT_TRUE(swapped_out_rwhv->did_swap_compositor_frame());
727 // Test if RenderViewHost::GetRenderWidgetHosts() only returns active
728 // widgets.
729 TEST_F(RenderFrameHostManagerTest, GetRenderWidgetHostsReturnsActiveViews) {
730 // This test is invalid in --site-per-process mode, as swapped-out is no
731 // longer used.
732 if (RenderFrameHostManager::IsSwappedOutStateForbidden()) {
733 return;
736 TestRenderFrameHost* swapped_out_rfh = CreateSwappedOutRenderFrameHost();
737 EXPECT_TRUE(swapped_out_rfh->is_swapped_out());
739 scoped_ptr<RenderWidgetHostIterator> widgets(
740 RenderWidgetHost::GetRenderWidgetHosts());
741 // We know that there is the only one active widget. Another view is
742 // now swapped out, so the swapped out view is not included in the
743 // list.
744 RenderWidgetHost* widget = widgets->GetNextHost();
745 EXPECT_FALSE(widgets->GetNextHost());
746 RenderViewHost* rvh = RenderViewHost::From(widget);
747 EXPECT_TRUE(static_cast<RenderViewHostImpl*>(rvh)->is_active());
750 // Test if RenderViewHost::GetRenderWidgetHosts() returns a subset of
751 // RenderViewHostImpl::GetAllRenderWidgetHosts().
752 // RenderViewHost::GetRenderWidgetHosts() returns only active widgets, but
753 // RenderViewHostImpl::GetAllRenderWidgetHosts() returns everything
754 // including swapped out ones.
755 TEST_F(RenderFrameHostManagerTest,
756 GetRenderWidgetHostsWithinGetAllRenderWidgetHosts) {
757 // This test is invalid in --site-per-process mode, as swapped-out is no
758 // longer used.
759 if (RenderFrameHostManager::IsSwappedOutStateForbidden()) {
760 return;
763 TestRenderFrameHost* swapped_out_rfh = CreateSwappedOutRenderFrameHost();
764 EXPECT_TRUE(swapped_out_rfh->is_swapped_out());
766 scoped_ptr<RenderWidgetHostIterator> widgets(
767 RenderWidgetHost::GetRenderWidgetHosts());
769 while (RenderWidgetHost* w = widgets->GetNextHost()) {
770 bool found = false;
771 scoped_ptr<RenderWidgetHostIterator> all_widgets(
772 RenderWidgetHostImpl::GetAllRenderWidgetHosts());
773 while (RenderWidgetHost* widget = all_widgets->GetNextHost()) {
774 if (w == widget) {
775 found = true;
776 break;
779 EXPECT_TRUE(found);
783 // Test if SiteInstanceImpl::active_frame_count() is correctly updated
784 // as frames in a SiteInstance get swapped out and in.
785 TEST_F(RenderFrameHostManagerTest, ActiveFrameCountWhileSwappingInAndOut) {
786 const GURL kUrl1("http://www.google.com/");
787 const GURL kUrl2("http://www.chromium.org/");
789 // Navigate to an initial URL.
790 contents()->NavigateAndCommit(kUrl1);
791 TestRenderFrameHost* rfh1 = main_test_rfh();
793 SiteInstanceImpl* instance1 = rfh1->GetSiteInstance();
794 EXPECT_EQ(instance1->active_frame_count(), 1U);
796 // Create 2 new tabs and simulate them being the opener chain for the main
797 // tab. They should be in the same SiteInstance.
798 scoped_ptr<TestWebContents> opener1(
799 TestWebContents::Create(browser_context(), instance1));
800 contents()->SetOpener(opener1.get());
802 scoped_ptr<TestWebContents> opener2(
803 TestWebContents::Create(browser_context(), instance1));
804 opener1->SetOpener(opener2.get());
806 EXPECT_EQ(instance1->active_frame_count(), 3U);
808 // Navigate to a cross-site URL (different SiteInstance but same
809 // BrowsingInstance).
810 contents()->NavigateAndCommit(kUrl2);
811 TestRenderFrameHost* rfh2 = main_test_rfh();
812 SiteInstanceImpl* instance2 = rfh2->GetSiteInstance();
814 // rvh2 is on chromium.org which is different from google.com on
815 // which other tabs are.
816 EXPECT_EQ(instance2->active_frame_count(), 1U);
818 // There are two active views on google.com now.
819 EXPECT_EQ(instance1->active_frame_count(), 2U);
821 // Navigate to the original origin (google.com).
822 contents()->NavigateAndCommit(kUrl1);
824 EXPECT_EQ(instance1->active_frame_count(), 3U);
827 // This deletes a WebContents when the given RVH is deleted. This is
828 // only for testing whether deleting an RVH does not cause any UaF in
829 // other parts of the system. For now, this class is only used for the
830 // next test cases to detect the bug mentioned at
831 // http://crbug.com/259859.
832 class RenderViewHostDestroyer : public WebContentsObserver {
833 public:
834 RenderViewHostDestroyer(RenderViewHost* render_view_host,
835 WebContents* web_contents)
836 : WebContentsObserver(WebContents::FromRenderViewHost(render_view_host)),
837 render_view_host_(render_view_host),
838 web_contents_(web_contents) {}
840 void RenderViewDeleted(RenderViewHost* render_view_host) override {
841 if (render_view_host == render_view_host_)
842 delete web_contents_;
845 private:
846 RenderViewHost* render_view_host_;
847 WebContents* web_contents_;
849 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestroyer);
852 // Test if ShutdownRenderViewHostsInSiteInstance() does not touch any
853 // RenderWidget that has been freed while deleting a RenderViewHost in
854 // a previous iteration. This is a regression test for
855 // http://crbug.com/259859.
856 TEST_F(RenderFrameHostManagerTest,
857 DetectUseAfterFreeInShutdownRenderViewHostsInSiteInstance) {
858 const GURL kChromeURL("chrome://newtab");
859 const GURL kUrl1("http://www.google.com");
860 const GURL kUrl2("http://www.chromium.org");
862 // Navigate our first tab to a chrome url and then to the destination.
863 NavigateActiveAndCommit(kChromeURL);
864 TestRenderFrameHost* ntp_rfh = contents()->GetMainFrame();
866 // Create one more tab and navigate to kUrl1. web_contents is not
867 // wrapped as scoped_ptr since it intentionally deleted by destroyer
868 // below as part of this test.
869 TestWebContents* web_contents =
870 TestWebContents::Create(browser_context(), ntp_rfh->GetSiteInstance());
871 web_contents->NavigateAndCommit(kUrl1);
872 RenderViewHostDestroyer destroyer(ntp_rfh->GetRenderViewHost(),
873 web_contents);
875 // This causes the first tab to navigate to kUrl2, which destroys
876 // the ntp_rfh in ShutdownRenderViewHostsInSiteInstance(). When
877 // ntp_rfh is destroyed, it also destroys the RVHs in web_contents
878 // too. This can test whether
879 // SiteInstanceImpl::ShutdownRenderViewHostsInSiteInstance() can
880 // touch any object freed in this way or not while iterating through
881 // all widgets.
882 contents()->NavigateAndCommit(kUrl2);
885 // When there is an error with the specified page, renderer exits view-source
886 // mode. See WebFrameImpl::DidFail(). We check by this test that
887 // EnableViewSourceMode message is sent on every navigation regardless
888 // RenderView is being newly created or reused.
889 TEST_F(RenderFrameHostManagerTest, AlwaysSendEnableViewSourceMode) {
890 const GURL kChromeUrl("chrome://foo/");
891 const GURL kUrl("http://foo/");
892 const GURL kViewSourceUrl("view-source:http://foo/");
894 // We have to navigate to some page at first since without this, the first
895 // navigation will reuse the SiteInstance created by Init(), and the second
896 // one will create a new SiteInstance. Because current_instance and
897 // new_instance will be different, a new RenderViewHost will be created for
898 // the second navigation. We have to avoid this in order to exercise the
899 // target code path.
900 NavigateActiveAndCommit(kChromeUrl);
902 // Navigate. Note that "view source" URLs are implemented by putting the RFH
903 // into a view-source mode and then navigating to the inner URL, so that's why
904 // the bare URL is what's committed and returned by the last committed entry's
905 // GetURL() call.
906 controller().LoadURL(
907 kViewSourceUrl, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
908 int entry_id = controller().GetPendingEntry()->GetUniqueID();
910 // Simulate response from RenderFrame for DispatchBeforeUnload.
911 contents()->GetMainFrame()->PrepareForCommit();
912 ASSERT_TRUE(contents()->GetPendingMainFrame())
913 << "Expected new pending RenderFrameHost to be created.";
914 RenderFrameHost* last_rfh = contents()->GetPendingMainFrame();
915 int32 new_id =
916 contents()->GetMaxPageIDForSiteInstance(last_rfh->GetSiteInstance()) + 1;
917 contents()->GetPendingMainFrame()->SendNavigate(new_id, entry_id, true, kUrl);
919 EXPECT_EQ(1, controller().GetLastCommittedEntryIndex());
920 NavigationEntry* last_committed = controller().GetLastCommittedEntry();
921 ASSERT_NE(nullptr, last_committed);
922 EXPECT_EQ(kUrl, last_committed->GetURL());
923 EXPECT_EQ(kViewSourceUrl, last_committed->GetVirtualURL());
924 EXPECT_FALSE(controller().GetPendingEntry());
925 // Because we're using TestWebContents and TestRenderViewHost in this
926 // unittest, no one calls WebContentsImpl::RenderViewCreated(). So, we see no
927 // EnableViewSourceMode message, here.
929 // Clear queued messages before load.
930 process()->sink().ClearMessages();
932 // Navigate, again.
933 controller().LoadURL(
934 kViewSourceUrl, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
935 entry_id = controller().GetPendingEntry()->GetUniqueID();
936 contents()->GetMainFrame()->PrepareForCommit();
938 // The same RenderViewHost should be reused.
939 EXPECT_FALSE(contents()->GetPendingMainFrame());
940 EXPECT_EQ(last_rfh, contents()->GetMainFrame());
942 // The renderer sends a commit.
943 contents()->GetMainFrame()->SendNavigateWithTransition(
944 new_id, entry_id, false, kUrl, ui::PAGE_TRANSITION_TYPED);
945 EXPECT_EQ(1, controller().GetLastCommittedEntryIndex());
946 EXPECT_FALSE(controller().GetPendingEntry());
948 // New message should be sent out to make sure to enter view-source mode.
949 EXPECT_TRUE(process()->sink().GetUniqueMessageMatching(
950 ViewMsg_EnableViewSourceMode::ID));
953 // Tests the Init function by checking the initial RenderViewHost.
954 TEST_F(RenderFrameHostManagerTest, Init) {
955 // Using TestBrowserContext.
956 SiteInstanceImpl* instance =
957 static_cast<SiteInstanceImpl*>(SiteInstance::Create(browser_context()));
958 EXPECT_FALSE(instance->HasSite());
960 scoped_ptr<TestWebContents> web_contents(
961 TestWebContents::Create(browser_context(), instance));
963 RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
964 RenderViewHostImpl* rvh = manager->current_host();
965 RenderFrameHostImpl* rfh = manager->current_frame_host();
966 ASSERT_TRUE(rvh);
967 ASSERT_TRUE(rfh);
968 EXPECT_EQ(rvh, rfh->render_view_host());
969 EXPECT_EQ(instance, rvh->GetSiteInstance());
970 EXPECT_EQ(web_contents.get(), rvh->GetDelegate());
971 EXPECT_EQ(web_contents.get(), rfh->delegate());
972 EXPECT_TRUE(manager->GetRenderWidgetHostView());
973 EXPECT_FALSE(manager->pending_render_view_host());
976 // Tests the Navigate function. We navigate three sites consecutively and check
977 // how the pending/committed RenderViewHost are modified.
978 TEST_F(RenderFrameHostManagerTest, Navigate) {
979 SiteInstance* instance = SiteInstance::Create(browser_context());
981 scoped_ptr<TestWebContents> web_contents(
982 TestWebContents::Create(browser_context(), instance));
983 RenderViewHostChangedObserver change_observer(web_contents.get());
985 RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
986 RenderFrameHostImpl* host = NULL;
988 // 1) The first navigation. --------------------------
989 const GURL kUrl1("http://www.google.com/");
990 NavigationEntryImpl entry1(
991 NULL /* instance */, -1 /* page_id */, kUrl1, Referrer(),
992 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED,
993 false /* is_renderer_init */);
994 host = NavigateToEntry(manager, entry1);
996 // The RenderFrameHost created in Init will be reused.
997 EXPECT_TRUE(host == manager->current_frame_host());
998 EXPECT_FALSE(GetPendingFrameHost(manager));
1000 // Commit.
1001 manager->DidNavigateFrame(host, true);
1002 // Commit to SiteInstance should be delayed until RenderFrame commit.
1003 EXPECT_TRUE(host == manager->current_frame_host());
1004 ASSERT_TRUE(host);
1005 EXPECT_FALSE(host->GetSiteInstance()->HasSite());
1006 host->GetSiteInstance()->SetSite(kUrl1);
1008 // 2) Navigate to next site. -------------------------
1009 const GURL kUrl2("http://www.google.com/foo");
1010 NavigationEntryImpl entry2(
1011 NULL /* instance */, -1 /* page_id */, kUrl2,
1012 Referrer(kUrl1, blink::WebReferrerPolicyDefault),
1013 base::string16() /* title */, ui::PAGE_TRANSITION_LINK,
1014 true /* is_renderer_init */);
1015 host = NavigateToEntry(manager, entry2);
1017 // The RenderFrameHost created in Init will be reused.
1018 EXPECT_TRUE(host == manager->current_frame_host());
1019 EXPECT_FALSE(GetPendingFrameHost(manager));
1021 // Commit.
1022 manager->DidNavigateFrame(host, true);
1023 EXPECT_TRUE(host == manager->current_frame_host());
1024 ASSERT_TRUE(host);
1025 EXPECT_TRUE(host->GetSiteInstance()->HasSite());
1027 // 3) Cross-site navigate to next site. --------------
1028 const GURL kUrl3("http://webkit.org/");
1029 NavigationEntryImpl entry3(
1030 NULL /* instance */, -1 /* page_id */, kUrl3,
1031 Referrer(kUrl2, blink::WebReferrerPolicyDefault),
1032 base::string16() /* title */, ui::PAGE_TRANSITION_LINK,
1033 false /* is_renderer_init */);
1034 host = NavigateToEntry(manager, entry3);
1036 // A new RenderFrameHost should be created.
1037 EXPECT_TRUE(GetPendingFrameHost(manager));
1038 ASSERT_EQ(host, GetPendingFrameHost(manager));
1040 change_observer.Reset();
1042 // Commit.
1043 manager->DidNavigateFrame(GetPendingFrameHost(manager), true);
1044 EXPECT_TRUE(host == manager->current_frame_host());
1045 ASSERT_TRUE(host);
1046 EXPECT_TRUE(host->GetSiteInstance()->HasSite());
1047 // Check the pending RenderFrameHost has been committed.
1048 EXPECT_FALSE(GetPendingFrameHost(manager));
1050 // We should observe RVH changed event.
1051 EXPECT_TRUE(change_observer.DidHostChange());
1054 // Tests WebUI creation.
1055 TEST_F(RenderFrameHostManagerTest, WebUI) {
1056 set_should_create_webui(true);
1057 SiteInstance* instance = SiteInstance::Create(browser_context());
1059 scoped_ptr<TestWebContents> web_contents(
1060 TestWebContents::Create(browser_context(), instance));
1061 RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
1062 RenderFrameHostImpl* initial_rfh = manager->current_frame_host();
1064 EXPECT_FALSE(manager->current_host()->IsRenderViewLive());
1065 EXPECT_FALSE(manager->web_ui());
1066 EXPECT_TRUE(initial_rfh);
1068 const GURL kUrl("chrome://foo");
1069 NavigationEntryImpl entry(NULL /* instance */, -1 /* page_id */, kUrl,
1070 Referrer(), base::string16() /* title */,
1071 ui::PAGE_TRANSITION_TYPED,
1072 false /* is_renderer_init */);
1073 RenderFrameHostImpl* host = NavigateToEntry(manager, entry);
1075 // We commit the pending RenderFrameHost immediately because the previous
1076 // RenderFrameHost was not live. We test a case where it is live in
1077 // WebUIInNewTab.
1078 EXPECT_TRUE(host);
1079 EXPECT_NE(initial_rfh, host);
1080 EXPECT_EQ(host, manager->current_frame_host());
1081 EXPECT_FALSE(GetPendingFrameHost(manager));
1083 // It's important that the SiteInstance get set on the Web UI page as soon
1084 // as the navigation starts, rather than lazily after it commits, so we don't
1085 // try to re-use the SiteInstance/process for non Web UI things that may
1086 // get loaded in between.
1087 EXPECT_TRUE(host->GetSiteInstance()->HasSite());
1088 EXPECT_EQ(kUrl, host->GetSiteInstance()->GetSiteURL());
1090 // The Web UI is committed immediately because the RenderViewHost has not been
1091 // used yet. UpdateStateForNavigate() took the short cut path.
1092 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1093 switches::kEnableBrowserSideNavigation)) {
1094 EXPECT_FALSE(manager->speculative_web_ui());
1095 } else {
1096 EXPECT_FALSE(manager->pending_web_ui());
1098 EXPECT_TRUE(manager->web_ui());
1100 // Commit.
1101 manager->DidNavigateFrame(host, true);
1102 EXPECT_TRUE(
1103 host->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1106 // Tests that we can open a WebUI link in a new tab from a WebUI page and still
1107 // grant the correct bindings. http://crbug.com/189101.
1108 TEST_F(RenderFrameHostManagerTest, WebUIInNewTab) {
1109 set_should_create_webui(true);
1110 SiteInstance* blank_instance = SiteInstance::Create(browser_context());
1111 blank_instance->GetProcess()->Init();
1113 // Create a blank tab.
1114 scoped_ptr<TestWebContents> web_contents1(
1115 TestWebContents::Create(browser_context(), blank_instance));
1116 RenderFrameHostManager* manager1 =
1117 web_contents1->GetRenderManagerForTesting();
1118 // Test the case that new RVH is considered live.
1119 manager1->current_host()->CreateRenderView(-1, MSG_ROUTING_NONE, -1,
1120 FrameReplicationState(), false);
1121 EXPECT_TRUE(manager1->current_host()->IsRenderViewLive());
1122 EXPECT_TRUE(manager1->current_frame_host()->IsRenderFrameLive());
1124 // Navigate to a WebUI page.
1125 const GURL kUrl1("chrome://foo");
1126 NavigationEntryImpl entry1(NULL /* instance */, -1 /* page_id */, kUrl1,
1127 Referrer(), base::string16() /* title */,
1128 ui::PAGE_TRANSITION_TYPED,
1129 false /* is_renderer_init */);
1130 RenderFrameHostImpl* host1 = NavigateToEntry(manager1, entry1);
1132 // We should have a pending navigation to the WebUI RenderViewHost.
1133 // It should already have bindings.
1134 EXPECT_EQ(host1, GetPendingFrameHost(manager1));
1135 EXPECT_NE(host1, manager1->current_frame_host());
1136 EXPECT_TRUE(
1137 host1->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1139 // Commit and ensure we still have bindings.
1140 manager1->DidNavigateFrame(host1, true);
1141 SiteInstance* webui_instance = host1->GetSiteInstance();
1142 EXPECT_EQ(host1, manager1->current_frame_host());
1143 EXPECT_TRUE(
1144 host1->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1146 // Now simulate clicking a link that opens in a new tab.
1147 scoped_ptr<TestWebContents> web_contents2(
1148 TestWebContents::Create(browser_context(), webui_instance));
1149 RenderFrameHostManager* manager2 =
1150 web_contents2->GetRenderManagerForTesting();
1151 // Make sure the new RVH is considered live. This is usually done in
1152 // RenderWidgetHost::Init when opening a new tab from a link.
1153 manager2->current_host()->CreateRenderView(-1, MSG_ROUTING_NONE, -1,
1154 FrameReplicationState(), false);
1155 EXPECT_TRUE(manager2->current_host()->IsRenderViewLive());
1157 const GURL kUrl2("chrome://foo/bar");
1158 NavigationEntryImpl entry2(NULL /* instance */, -1 /* page_id */, kUrl2,
1159 Referrer(), base::string16() /* title */,
1160 ui::PAGE_TRANSITION_LINK,
1161 true /* is_renderer_init */);
1162 RenderFrameHostImpl* host2 = NavigateToEntry(manager2, entry2);
1164 // No cross-process transition happens because we are already in the right
1165 // SiteInstance. We should grant bindings immediately.
1166 EXPECT_EQ(host2, manager2->current_frame_host());
1167 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1168 switches::kEnableBrowserSideNavigation)) {
1169 EXPECT_TRUE(manager2->speculative_web_ui());
1170 } else {
1171 EXPECT_TRUE(manager2->pending_web_ui());
1173 EXPECT_TRUE(
1174 host2->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1176 manager2->DidNavigateFrame(host2, true);
1179 // Tests that a WebUI is correctly reused between chrome:// pages.
1180 TEST_F(RenderFrameHostManagerTest, WebUIWasReused) {
1181 set_should_create_webui(true);
1183 // Navigate to a WebUI page.
1184 const GURL kUrl1("chrome://foo");
1185 contents()->NavigateAndCommit(kUrl1);
1186 RenderFrameHostManager* manager =
1187 main_test_rfh()->frame_tree_node()->render_manager();
1188 WebUIImpl* web_ui = manager->web_ui();
1189 EXPECT_TRUE(web_ui);
1191 // Navigate to another WebUI page which should be same-site and keep the
1192 // current WebUI.
1193 const GURL kUrl2("chrome://foo/bar");
1194 contents()->NavigateAndCommit(kUrl2);
1195 EXPECT_EQ(web_ui, manager->web_ui());
1198 // Tests that a WebUI is correctly cleaned up when navigating from a chrome://
1199 // page to a non-chrome:// page.
1200 TEST_F(RenderFrameHostManagerTest, WebUIWasCleared) {
1201 set_should_create_webui(true);
1203 // Navigate to a WebUI page.
1204 const GURL kUrl1("chrome://foo");
1205 contents()->NavigateAndCommit(kUrl1);
1206 EXPECT_TRUE(main_test_rfh()->frame_tree_node()->render_manager()->web_ui());
1208 // Navigate to a non-WebUI page.
1209 const GURL kUrl2("http://www.google.com");
1210 contents()->NavigateAndCommit(kUrl2);
1211 EXPECT_FALSE(main_test_rfh()->frame_tree_node()->render_manager()->web_ui());
1214 // Tests that we don't end up in an inconsistent state if a page does a back and
1215 // then reload. http://crbug.com/51680
1216 // Also tests that only user-gesture navigations can interrupt cross-process
1217 // navigations. http://crbug.com/75195
1218 TEST_F(RenderFrameHostManagerTest, PageDoesBackAndReload) {
1219 const GURL kUrl1("http://www.google.com/");
1220 const GURL kUrl2("http://www.evil-site.com/");
1222 // Navigate to a safe site, then an evil site.
1223 // This will switch RenderFrameHosts. We cannot assert that the first and
1224 // second RFHs are different, though, because the first one may be promptly
1225 // deleted.
1226 contents()->NavigateAndCommit(kUrl1);
1227 contents()->NavigateAndCommit(kUrl2);
1228 TestRenderFrameHost* evil_rfh = contents()->GetMainFrame();
1230 // Now let's simulate the evil page calling history.back().
1231 contents()->OnGoToEntryAtOffset(-1);
1232 contents()->GetMainFrame()->PrepareForCommit();
1233 // We should have a new pending RFH.
1234 // Note that in this case, the navigation has not committed, so evil_rfh will
1235 // not be deleted yet.
1236 EXPECT_NE(evil_rfh, contents()->GetPendingMainFrame());
1237 EXPECT_NE(evil_rfh->GetRenderViewHost(),
1238 contents()->GetPendingMainFrame()->GetRenderViewHost());
1240 // Before that RFH has committed, the evil page reloads itself.
1241 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1242 params.page_id = 0;
1243 params.nav_entry_id = 0;
1244 params.did_create_new_entry = false;
1245 params.url = kUrl2;
1246 params.transition = ui::PAGE_TRANSITION_CLIENT_REDIRECT;
1247 params.should_update_history = false;
1248 params.gesture = NavigationGestureAuto;
1249 params.was_within_same_page = false;
1250 params.is_post = false;
1251 params.page_state = PageState::CreateFromURL(kUrl2);
1253 contents()->GetFrameTree()->root()->navigator()->DidNavigate(evil_rfh,
1254 params);
1256 // That should NOT have cancelled the pending RFH, because the reload did
1257 // not have a user gesture. Thus, the pending back navigation will still
1258 // eventually commit.
1259 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
1260 pending_render_view_host() != NULL);
1261 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->pending_frame_host() !=
1262 NULL);
1263 EXPECT_EQ(evil_rfh,
1264 contents()->GetRenderManagerForTesting()->current_frame_host());
1265 EXPECT_EQ(evil_rfh->GetRenderViewHost(),
1266 contents()->GetRenderManagerForTesting()->current_host());
1268 // Also we should not have a pending navigation entry.
1269 EXPECT_TRUE(contents()->GetController().GetPendingEntry() == NULL);
1270 NavigationEntry* entry = contents()->GetController().GetVisibleEntry();
1271 ASSERT_TRUE(entry != NULL);
1272 EXPECT_EQ(kUrl2, entry->GetURL());
1274 // Now do the same but as a user gesture.
1275 params.gesture = NavigationGestureUser;
1276 contents()->GetFrameTree()->root()->navigator()->DidNavigate(evil_rfh,
1277 params);
1279 // User navigation should have cancelled the pending RFH.
1280 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
1281 pending_render_view_host() == NULL);
1282 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->pending_frame_host() ==
1283 NULL);
1284 EXPECT_EQ(evil_rfh,
1285 contents()->GetRenderManagerForTesting()->current_frame_host());
1286 EXPECT_EQ(evil_rfh->GetRenderViewHost(),
1287 contents()->GetRenderManagerForTesting()->current_host());
1289 // Also we should not have a pending navigation entry.
1290 EXPECT_TRUE(contents()->GetController().GetPendingEntry() == NULL);
1291 entry = contents()->GetController().GetVisibleEntry();
1292 ASSERT_TRUE(entry != NULL);
1293 EXPECT_EQ(kUrl2, entry->GetURL());
1296 // Ensure that we can go back and forward even if a SwapOut ACK isn't received.
1297 // See http://crbug.com/93427.
1298 TEST_F(RenderFrameHostManagerTest, NavigateAfterMissingSwapOutACK) {
1299 const GURL kUrl1("http://www.google.com/");
1300 const GURL kUrl2("http://www.chromium.org/");
1302 // Navigate to two pages.
1303 contents()->NavigateAndCommit(kUrl1);
1304 TestRenderFrameHost* rfh1 = main_test_rfh();
1306 // Keep active_frame_count nonzero so that no swapped out frames in
1307 // this SiteInstance get forcefully deleted.
1308 rfh1->GetSiteInstance()->increment_active_frame_count();
1310 contents()->NavigateAndCommit(kUrl2);
1311 TestRenderFrameHost* rfh2 = main_test_rfh();
1312 rfh2->GetSiteInstance()->increment_active_frame_count();
1314 // Now go back, but suppose the SwapOut_ACK isn't received. This shouldn't
1315 // happen, but we have seen it when going back quickly across many entries
1316 // (http://crbug.com/93427).
1317 contents()->GetController().GoBack();
1318 EXPECT_TRUE(rfh2->is_waiting_for_beforeunload_ack());
1319 contents()->GetMainFrame()->PrepareForCommit();
1320 EXPECT_FALSE(rfh2->is_waiting_for_beforeunload_ack());
1322 // The back navigation commits.
1323 const NavigationEntry* entry1 = contents()->GetController().GetPendingEntry();
1324 contents()->GetPendingMainFrame()->SendNavigate(
1325 entry1->GetPageID(), entry1->GetUniqueID(), false, entry1->GetURL());
1326 EXPECT_TRUE(rfh2->IsWaitingForUnloadACK());
1327 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT, rfh2->rfh_state());
1329 // We should be able to navigate forward.
1330 contents()->GetController().GoForward();
1331 contents()->GetMainFrame()->PrepareForCommit();
1332 const NavigationEntry* entry2 = contents()->GetController().GetPendingEntry();
1333 contents()->GetPendingMainFrame()->SendNavigate(
1334 entry2->GetPageID(), entry2->GetUniqueID(), false, entry2->GetURL());
1335 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, main_test_rfh()->rfh_state());
1336 if (!RenderFrameHostManager::IsSwappedOutStateForbidden()) {
1337 EXPECT_EQ(rfh2, main_test_rfh());
1338 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT, rfh1->rfh_state());
1339 rfh1->OnSwappedOut();
1340 EXPECT_TRUE(rfh1->is_swapped_out());
1341 EXPECT_EQ(RenderFrameHostImpl::STATE_SWAPPED_OUT, rfh1->rfh_state());
1345 // Test that we create swapped out RFHs for the opener chain when navigating an
1346 // opened tab cross-process. This allows us to support certain cross-process
1347 // JavaScript calls (http://crbug.com/99202).
1348 TEST_F(RenderFrameHostManagerTest, CreateSwappedOutOpenerRFHs) {
1349 const GURL kUrl1("http://www.google.com/");
1350 const GURL kUrl2("http://www.chromium.org/");
1351 const GURL kChromeUrl("chrome://foo");
1353 // Navigate to an initial URL.
1354 contents()->NavigateAndCommit(kUrl1);
1355 RenderFrameHostManager* manager = contents()->GetRenderManagerForTesting();
1356 TestRenderFrameHost* rfh1 = main_test_rfh();
1357 scoped_refptr<SiteInstanceImpl> site_instance1 = rfh1->GetSiteInstance();
1358 RenderFrameDeletedObserver rfh1_deleted_observer(rfh1);
1359 TestRenderViewHost* rvh1 = test_rvh();
1361 // Create 2 new tabs and simulate them being the opener chain for the main
1362 // tab. They should be in the same SiteInstance.
1363 scoped_ptr<TestWebContents> opener1(
1364 TestWebContents::Create(browser_context(), site_instance1.get()));
1365 RenderFrameHostManager* opener1_manager =
1366 opener1->GetRenderManagerForTesting();
1367 contents()->SetOpener(opener1.get());
1369 scoped_ptr<TestWebContents> opener2(
1370 TestWebContents::Create(browser_context(), site_instance1.get()));
1371 RenderFrameHostManager* opener2_manager =
1372 opener2->GetRenderManagerForTesting();
1373 opener1->SetOpener(opener2.get());
1375 // Navigate to a cross-site URL (different SiteInstance but same
1376 // BrowsingInstance).
1377 contents()->NavigateAndCommit(kUrl2);
1378 TestRenderFrameHost* rfh2 = main_test_rfh();
1379 TestRenderViewHost* rvh2 = test_rvh();
1380 EXPECT_NE(site_instance1, rfh2->GetSiteInstance());
1381 EXPECT_TRUE(site_instance1->IsRelatedSiteInstance(rfh2->GetSiteInstance()));
1383 // Ensure rvh1 is placed on swapped out list of the current tab.
1384 if (!RenderFrameHostManager::IsSwappedOutStateForbidden()) {
1385 EXPECT_TRUE(manager->IsRVHOnSwappedOutList(rvh1));
1386 EXPECT_FALSE(rfh1_deleted_observer.deleted());
1387 EXPECT_TRUE(manager->IsOnSwappedOutList(rfh1));
1388 EXPECT_EQ(rfh1,
1389 manager->GetRenderFrameProxyHost(site_instance1.get())
1390 ->render_frame_host());
1391 } else {
1392 EXPECT_TRUE(rfh1_deleted_observer.deleted());
1393 EXPECT_TRUE(manager->GetRenderFrameProxyHost(site_instance1.get()));
1395 EXPECT_EQ(rvh1,
1396 manager->GetSwappedOutRenderViewHost(rvh1->GetSiteInstance()));
1398 // Ensure a swapped out RFH and RFH is created in the first opener tab.
1399 RenderFrameProxyHost* opener1_proxy =
1400 opener1_manager->GetRenderFrameProxyHost(rfh2->GetSiteInstance());
1401 RenderFrameHostImpl* opener1_rfh = opener1_proxy->render_frame_host();
1402 TestRenderViewHost* opener1_rvh = static_cast<TestRenderViewHost*>(
1403 opener1_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance()));
1404 if (!RenderFrameHostManager::IsSwappedOutStateForbidden()) {
1405 EXPECT_TRUE(opener1_manager->IsOnSwappedOutList(opener1_rfh));
1406 EXPECT_TRUE(opener1_manager->IsRVHOnSwappedOutList(opener1_rvh));
1407 EXPECT_TRUE(opener1_rfh->is_swapped_out());
1408 } else {
1409 EXPECT_FALSE(opener1_rfh);
1411 EXPECT_FALSE(opener1_rvh->is_active());
1413 // Ensure a swapped out RFH and RVH is created in the second opener tab.
1414 RenderFrameProxyHost* opener2_proxy =
1415 opener2_manager->GetRenderFrameProxyHost(rfh2->GetSiteInstance());
1416 RenderFrameHostImpl* opener2_rfh = opener2_proxy->render_frame_host();
1417 TestRenderViewHost* opener2_rvh = static_cast<TestRenderViewHost*>(
1418 opener2_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance()));
1419 if (!RenderFrameHostManager::IsSwappedOutStateForbidden()) {
1420 EXPECT_TRUE(opener2_manager->IsOnSwappedOutList(opener2_rfh));
1421 EXPECT_TRUE(opener2_manager->IsRVHOnSwappedOutList(opener2_rvh));
1422 EXPECT_TRUE(opener2_rfh->is_swapped_out());
1423 } else {
1424 EXPECT_FALSE(opener2_rfh);
1426 EXPECT_FALSE(opener2_rvh->is_active());
1428 // Navigate to a cross-BrowsingInstance URL.
1429 contents()->NavigateAndCommit(kChromeUrl);
1430 TestRenderFrameHost* rfh3 = main_test_rfh();
1431 EXPECT_NE(site_instance1, rfh3->GetSiteInstance());
1432 EXPECT_FALSE(site_instance1->IsRelatedSiteInstance(rfh3->GetSiteInstance()));
1434 // No scripting is allowed across BrowsingInstances, so we should not create
1435 // swapped out RVHs for the opener chain in this case.
1436 EXPECT_FALSE(opener1_manager->GetRenderFrameProxyHost(
1437 rfh3->GetSiteInstance()));
1438 EXPECT_FALSE(opener1_manager->GetSwappedOutRenderViewHost(
1439 rfh3->GetSiteInstance()));
1440 EXPECT_FALSE(opener2_manager->GetRenderFrameProxyHost(
1441 rfh3->GetSiteInstance()));
1442 EXPECT_FALSE(opener2_manager->GetSwappedOutRenderViewHost(
1443 rfh3->GetSiteInstance()));
1446 // Test that a page can disown the opener of the WebContents.
1447 TEST_F(RenderFrameHostManagerTest, DisownOpener) {
1448 const GURL kUrl1("http://www.google.com/");
1449 const GURL kUrl2("http://www.chromium.org/");
1451 // Navigate to an initial URL.
1452 contents()->NavigateAndCommit(kUrl1);
1453 TestRenderFrameHost* rfh1 = main_test_rfh();
1454 scoped_refptr<SiteInstanceImpl> site_instance1 = rfh1->GetSiteInstance();
1456 // Create a new tab and simulate having it be the opener for the main tab.
1457 scoped_ptr<TestWebContents> opener1(
1458 TestWebContents::Create(browser_context(), rfh1->GetSiteInstance()));
1459 contents()->SetOpener(opener1.get());
1460 EXPECT_TRUE(contents()->HasOpener());
1462 // Navigate to a cross-site URL (different SiteInstance but same
1463 // BrowsingInstance).
1464 contents()->NavigateAndCommit(kUrl2);
1465 TestRenderFrameHost* rfh2 = main_test_rfh();
1466 EXPECT_NE(site_instance1, rfh2->GetSiteInstance());
1468 // Disown the opener from rfh2.
1469 rfh2->DidDisownOpener();
1471 // Ensure the opener is cleared.
1472 EXPECT_FALSE(contents()->HasOpener());
1475 // Test that a page can disown a same-site opener of the WebContents.
1476 TEST_F(RenderFrameHostManagerTest, DisownSameSiteOpener) {
1477 const GURL kUrl1("http://www.google.com/");
1479 // Navigate to an initial URL.
1480 contents()->NavigateAndCommit(kUrl1);
1481 TestRenderFrameHost* rfh1 = main_test_rfh();
1483 // Create a new tab and simulate having it be the opener for the main tab.
1484 scoped_ptr<TestWebContents> opener1(
1485 TestWebContents::Create(browser_context(), rfh1->GetSiteInstance()));
1486 contents()->SetOpener(opener1.get());
1487 EXPECT_TRUE(contents()->HasOpener());
1489 // Disown the opener from rfh1.
1490 rfh1->DidDisownOpener();
1492 // Ensure the opener is cleared even if it is in the same process.
1493 EXPECT_FALSE(contents()->HasOpener());
1496 // Test that a page can disown the opener just as a cross-process navigation is
1497 // in progress.
1498 TEST_F(RenderFrameHostManagerTest, DisownOpenerDuringNavigation) {
1499 const GURL kUrl1("http://www.google.com/");
1500 const GURL kUrl2("http://www.chromium.org/");
1502 // Navigate to an initial URL.
1503 contents()->NavigateAndCommit(kUrl1);
1504 scoped_refptr<SiteInstanceImpl> site_instance1 =
1505 main_test_rfh()->GetSiteInstance();
1507 // Create a new tab and simulate having it be the opener for the main tab.
1508 scoped_ptr<TestWebContents> opener1(
1509 TestWebContents::Create(browser_context(), site_instance1.get()));
1510 contents()->SetOpener(opener1.get());
1511 EXPECT_TRUE(contents()->HasOpener());
1513 // Navigate to a cross-site URL (different SiteInstance but same
1514 // BrowsingInstance).
1515 contents()->NavigateAndCommit(kUrl2);
1516 TestRenderFrameHost* rfh2 = main_test_rfh();
1517 EXPECT_NE(site_instance1, rfh2->GetSiteInstance());
1519 // Start a back navigation.
1520 contents()->GetController().GoBack();
1521 contents()->GetMainFrame()->PrepareForCommit();
1523 // Disown the opener from rfh2.
1524 rfh2->DidDisownOpener();
1526 // Ensure the opener is cleared.
1527 EXPECT_FALSE(contents()->HasOpener());
1529 // The back navigation commits.
1530 const NavigationEntry* entry1 = contents()->GetController().GetPendingEntry();
1531 contents()->GetPendingMainFrame()->SendNavigate(
1532 entry1->GetPageID(), entry1->GetUniqueID(), false, entry1->GetURL());
1534 // Ensure the opener is still cleared.
1535 EXPECT_FALSE(contents()->HasOpener());
1538 // Test that a page can disown the opener just after a cross-process navigation
1539 // commits.
1540 TEST_F(RenderFrameHostManagerTest, DisownOpenerAfterNavigation) {
1541 const GURL kUrl1("http://www.google.com/");
1542 const GURL kUrl2("http://www.chromium.org/");
1544 // Navigate to an initial URL.
1545 contents()->NavigateAndCommit(kUrl1);
1546 scoped_refptr<SiteInstanceImpl> site_instance1 =
1547 main_test_rfh()->GetSiteInstance();
1549 // Create a new tab and simulate having it be the opener for the main tab.
1550 scoped_ptr<TestWebContents> opener1(
1551 TestWebContents::Create(browser_context(), site_instance1.get()));
1552 contents()->SetOpener(opener1.get());
1553 EXPECT_TRUE(contents()->HasOpener());
1555 // Navigate to a cross-site URL (different SiteInstance but same
1556 // BrowsingInstance).
1557 contents()->NavigateAndCommit(kUrl2);
1558 TestRenderFrameHost* rfh2 = main_test_rfh();
1559 EXPECT_NE(site_instance1, rfh2->GetSiteInstance());
1561 // Commit a back navigation before the DidDisownOpener message arrives.
1562 contents()->GetController().GoBack();
1563 contents()->GetMainFrame()->PrepareForCommit();
1564 const NavigationEntry* entry1 = contents()->GetController().GetPendingEntry();
1565 contents()->GetPendingMainFrame()->SendNavigate(
1566 entry1->GetPageID(), entry1->GetUniqueID(), false, entry1->GetURL());
1568 // Disown the opener from rfh2.
1569 rfh2->DidDisownOpener();
1570 EXPECT_FALSE(contents()->HasOpener());
1573 // Test that we clean up swapped out RenderViewHosts when a process hosting
1574 // those associated RenderViews crashes. http://crbug.com/258993
1575 TEST_F(RenderFrameHostManagerTest, CleanUpSwappedOutRVHOnProcessCrash) {
1576 const GURL kUrl1("http://www.google.com/");
1577 const GURL kUrl2("http://www.chromium.org/");
1579 // Navigate to an initial URL.
1580 contents()->NavigateAndCommit(kUrl1);
1581 TestRenderFrameHost* rfh1 = contents()->GetMainFrame();
1583 // Create a new tab as an opener for the main tab.
1584 scoped_ptr<TestWebContents> opener1(
1585 TestWebContents::Create(browser_context(), rfh1->GetSiteInstance()));
1586 RenderFrameHostManager* opener1_manager =
1587 opener1->GetRenderManagerForTesting();
1588 contents()->SetOpener(opener1.get());
1590 // Make sure the new opener RVH is considered live.
1591 opener1_manager->current_host()->CreateRenderView(
1592 -1, MSG_ROUTING_NONE, -1, FrameReplicationState(), false);
1593 EXPECT_TRUE(opener1_manager->current_host()->IsRenderViewLive());
1594 EXPECT_TRUE(opener1_manager->current_frame_host()->IsRenderFrameLive());
1596 // Use a cross-process navigation in the opener to swap out the old RVH.
1597 EXPECT_FALSE(
1598 opener1_manager->GetSwappedOutRenderViewHost(rfh1->GetSiteInstance()));
1599 opener1->NavigateAndCommit(kUrl2);
1600 RenderViewHostImpl* swapped_out_rvh =
1601 opener1_manager->GetSwappedOutRenderViewHost(rfh1->GetSiteInstance());
1602 EXPECT_TRUE(swapped_out_rvh);
1603 EXPECT_TRUE(swapped_out_rvh->is_swapped_out_);
1604 EXPECT_FALSE(swapped_out_rvh->is_active());
1606 // Fake a process crash.
1607 rfh1->GetProcess()->SimulateCrash();
1609 // Ensure that the RenderFrameProxyHost stays around and the RenderFrameProxy
1610 // is deleted.
1611 RenderFrameProxyHost* render_frame_proxy_host =
1612 opener1_manager->GetRenderFrameProxyHost(rfh1->GetSiteInstance());
1613 EXPECT_TRUE(render_frame_proxy_host);
1614 EXPECT_FALSE(render_frame_proxy_host->is_render_frame_proxy_live());
1616 // Expect the swapped out RVH to exist but not be live.
1617 EXPECT_TRUE(
1618 opener1_manager->GetSwappedOutRenderViewHost(rfh1->GetSiteInstance()));
1619 EXPECT_FALSE(
1620 opener1_manager->GetSwappedOutRenderViewHost(rfh1->GetSiteInstance())
1621 ->IsRenderViewLive());
1623 // Reload the initial tab. This should recreate the opener's swapped out RVH
1624 // in the original SiteInstance.
1625 contents()->GetController().Reload(true);
1626 contents()->GetMainFrame()->PrepareForCommit();
1627 EXPECT_TRUE(
1628 opener1_manager->GetSwappedOutRenderViewHost(rfh1->GetSiteInstance())
1629 ->IsRenderViewLive());
1630 EXPECT_EQ(
1631 opener1_manager->GetRoutingIdForSiteInstance(rfh1->GetSiteInstance()),
1632 contents()->GetMainFrame()->GetRenderViewHost()->opener_frame_route_id());
1635 // Test that RenderViewHosts created for WebUI navigations are properly
1636 // granted WebUI bindings even if an unprivileged swapped out RenderViewHost
1637 // is in the same process (http://crbug.com/79918).
1638 TEST_F(RenderFrameHostManagerTest, EnableWebUIWithSwappedOutOpener) {
1639 set_should_create_webui(true);
1640 const GURL kSettingsUrl("chrome://chrome/settings");
1641 const GURL kPluginUrl("chrome://plugins");
1643 // Navigate to an initial WebUI URL.
1644 contents()->NavigateAndCommit(kSettingsUrl);
1646 // Ensure the RVH has WebUI bindings.
1647 TestRenderViewHost* rvh1 = test_rvh();
1648 EXPECT_TRUE(rvh1->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1650 // Create a new tab and simulate it being the opener for the main
1651 // tab. It should be in the same SiteInstance.
1652 scoped_ptr<TestWebContents> opener1(
1653 TestWebContents::Create(browser_context(), rvh1->GetSiteInstance()));
1654 RenderFrameHostManager* opener1_manager =
1655 opener1->GetRenderManagerForTesting();
1656 contents()->SetOpener(opener1.get());
1658 // Navigate to a different WebUI URL (different SiteInstance, same
1659 // BrowsingInstance).
1660 contents()->NavigateAndCommit(kPluginUrl);
1661 TestRenderViewHost* rvh2 = test_rvh();
1662 EXPECT_NE(rvh1->GetSiteInstance(), rvh2->GetSiteInstance());
1663 EXPECT_TRUE(rvh1->GetSiteInstance()->IsRelatedSiteInstance(
1664 rvh2->GetSiteInstance()));
1666 // Ensure a swapped out RFH and RVH is created in the first opener tab.
1667 RenderFrameProxyHost* opener1_proxy =
1668 opener1_manager->GetRenderFrameProxyHost(rvh2->GetSiteInstance());
1669 RenderFrameHostImpl* opener1_rfh = opener1_proxy->render_frame_host();
1670 TestRenderViewHost* opener1_rvh = static_cast<TestRenderViewHost*>(
1671 opener1_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance()));
1672 if (!RenderFrameHostManager::IsSwappedOutStateForbidden()) {
1673 EXPECT_TRUE(opener1_manager->IsOnSwappedOutList(opener1_rfh));
1674 EXPECT_TRUE(opener1_manager->IsRVHOnSwappedOutList(opener1_rvh));
1675 EXPECT_TRUE(opener1_rfh->is_swapped_out());
1676 } else {
1677 EXPECT_FALSE(opener1_rfh);
1679 EXPECT_FALSE(opener1_rvh->is_active());
1681 // Ensure the new RVH has WebUI bindings.
1682 EXPECT_TRUE(rvh2->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1685 // Test that we reuse the same guest SiteInstance if we navigate across sites.
1686 TEST_F(RenderFrameHostManagerTest, NoSwapOnGuestNavigations) {
1687 GURL guest_url(std::string(kGuestScheme).append("://abc123"));
1688 SiteInstance* instance =
1689 SiteInstance::CreateForURL(browser_context(), guest_url);
1690 scoped_ptr<TestWebContents> web_contents(
1691 TestWebContents::Create(browser_context(), instance));
1693 RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
1695 RenderFrameHostImpl* host = NULL;
1697 // 1) The first navigation. --------------------------
1698 const GURL kUrl1("http://www.google.com/");
1699 NavigationEntryImpl entry1(
1700 NULL /* instance */, -1 /* page_id */, kUrl1, Referrer(),
1701 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED,
1702 false /* is_renderer_init */);
1703 host = NavigateToEntry(manager, entry1);
1705 // The RenderFrameHost created in Init will be reused.
1706 EXPECT_TRUE(host == manager->current_frame_host());
1707 EXPECT_FALSE(manager->pending_frame_host());
1708 EXPECT_EQ(manager->current_frame_host()->GetSiteInstance(), instance);
1710 // Commit.
1711 manager->DidNavigateFrame(host, true);
1712 // Commit to SiteInstance should be delayed until RenderFrame commit.
1713 EXPECT_EQ(host, manager->current_frame_host());
1714 ASSERT_TRUE(host);
1715 EXPECT_TRUE(host->GetSiteInstance()->HasSite());
1717 // 2) Navigate to a different domain. -------------------------
1718 // Guests stay in the same process on navigation.
1719 const GURL kUrl2("http://www.chromium.org");
1720 NavigationEntryImpl entry2(
1721 NULL /* instance */, -1 /* page_id */, kUrl2,
1722 Referrer(kUrl1, blink::WebReferrerPolicyDefault),
1723 base::string16() /* title */, ui::PAGE_TRANSITION_LINK,
1724 true /* is_renderer_init */);
1725 host = NavigateToEntry(manager, entry2);
1727 // The RenderFrameHost created in Init will be reused.
1728 EXPECT_EQ(host, manager->current_frame_host());
1729 EXPECT_FALSE(manager->pending_frame_host());
1731 // Commit.
1732 manager->DidNavigateFrame(host, true);
1733 EXPECT_EQ(host, manager->current_frame_host());
1734 ASSERT_TRUE(host);
1735 EXPECT_EQ(host->GetSiteInstance(), instance);
1738 // Test that we cancel a pending RVH if we close the tab while it's pending.
1739 // http://crbug.com/294697.
1740 TEST_F(RenderFrameHostManagerTest, NavigateWithEarlyClose) {
1741 TestNotificationTracker notifications;
1743 SiteInstance* instance = SiteInstance::Create(browser_context());
1745 BeforeUnloadFiredWebContentsDelegate delegate;
1746 scoped_ptr<TestWebContents> web_contents(
1747 TestWebContents::Create(browser_context(), instance));
1748 RenderViewHostChangedObserver change_observer(web_contents.get());
1749 web_contents->SetDelegate(&delegate);
1751 RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
1753 // 1) The first navigation. --------------------------
1754 const GURL kUrl1("http://www.google.com/");
1755 NavigationEntryImpl entry1(NULL /* instance */, -1 /* page_id */, kUrl1,
1756 Referrer(), base::string16() /* title */,
1757 ui::PAGE_TRANSITION_TYPED,
1758 false /* is_renderer_init */);
1759 RenderFrameHostImpl* host = NavigateToEntry(manager, entry1);
1761 // The RenderFrameHost created in Init will be reused.
1762 EXPECT_EQ(host, manager->current_frame_host());
1763 EXPECT_FALSE(GetPendingFrameHost(manager));
1765 // We should observe RVH changed event.
1766 EXPECT_TRUE(change_observer.DidHostChange());
1768 // Commit.
1769 manager->DidNavigateFrame(host, true);
1771 // Commit to SiteInstance should be delayed until RenderFrame commits.
1772 EXPECT_EQ(host, manager->current_frame_host());
1773 EXPECT_FALSE(host->GetSiteInstance()->HasSite());
1774 host->GetSiteInstance()->SetSite(kUrl1);
1776 // 2) Cross-site navigate to next site. -------------------------
1777 const GURL kUrl2("http://www.example.com");
1778 NavigationEntryImpl entry2(
1779 NULL /* instance */, -1 /* page_id */, kUrl2, Referrer(),
1780 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED,
1781 false /* is_renderer_init */);
1782 RenderFrameHostImpl* host2 = NavigateToEntry(manager, entry2);
1784 // A new RenderFrameHost should be created.
1785 ASSERT_EQ(host2, GetPendingFrameHost(manager));
1786 EXPECT_NE(host2, host);
1788 EXPECT_EQ(host, manager->current_frame_host());
1789 EXPECT_FALSE(manager->current_frame_host()->is_swapped_out());
1790 EXPECT_EQ(host2, GetPendingFrameHost(manager));
1792 // 3) Close the tab. -------------------------
1793 notifications.ListenFor(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
1794 Source<RenderWidgetHost>(host2->render_view_host()));
1795 manager->OnBeforeUnloadACK(false, true, base::TimeTicks());
1797 EXPECT_TRUE(
1798 notifications.Check1AndReset(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED));
1799 EXPECT_FALSE(GetPendingFrameHost(manager));
1800 EXPECT_EQ(host, manager->current_frame_host());
1803 TEST_F(RenderFrameHostManagerTest, CloseWithPendingWhileUnresponsive) {
1804 const GURL kUrl1("http://www.google.com/");
1805 const GURL kUrl2("http://www.chromium.org/");
1807 CloseWebContentsDelegate close_delegate;
1808 contents()->SetDelegate(&close_delegate);
1810 // Navigate to the first page.
1811 contents()->NavigateAndCommit(kUrl1);
1812 TestRenderFrameHost* rfh1 = contents()->GetMainFrame();
1814 // Start to close the tab, but assume it's unresponsive.
1815 rfh1->render_view_host()->ClosePage();
1816 EXPECT_TRUE(rfh1->render_view_host()->is_waiting_for_close_ack());
1818 // Start a navigation to a new site.
1819 controller().LoadURL(
1820 kUrl2, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
1821 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1822 switches::kEnableBrowserSideNavigation)) {
1823 rfh1->PrepareForCommit();
1825 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1827 // Simulate the unresponsiveness timer. The tab should close.
1828 contents()->RendererUnresponsive(rfh1->render_view_host());
1829 EXPECT_TRUE(close_delegate.is_closed());
1832 // Tests that the RenderFrameHost is properly deleted when the SwapOutACK is
1833 // received. (SwapOut and the corresponding ACK always occur after commit.)
1834 // Also tests that an early SwapOutACK is properly ignored.
1835 TEST_F(RenderFrameHostManagerTest, DeleteFrameAfterSwapOutACK) {
1836 const GURL kUrl1("http://www.google.com/");
1837 const GURL kUrl2("http://www.chromium.org/");
1839 // Navigate to the first page.
1840 contents()->NavigateAndCommit(kUrl1);
1841 TestRenderFrameHost* rfh1 = contents()->GetMainFrame();
1842 RenderFrameDeletedObserver rfh_deleted_observer(rfh1);
1843 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh1->rfh_state());
1845 // Navigate to new site, simulating onbeforeunload approval.
1846 controller().LoadURL(
1847 kUrl2, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
1848 int entry_id = controller().GetPendingEntry()->GetUniqueID();
1849 contents()->GetMainFrame()->PrepareForCommit();
1850 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1851 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh1->rfh_state());
1852 TestRenderFrameHost* rfh2 = contents()->GetPendingMainFrame();
1854 // Simulate the swap out ack, unexpectedly early (before commit). It should
1855 // have no effect.
1856 rfh1->OnSwappedOut();
1857 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1858 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh1->rfh_state());
1860 // The new page commits.
1861 contents()->TestDidNavigate(rfh2, 1, entry_id, true, kUrl2,
1862 ui::PAGE_TRANSITION_TYPED);
1863 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1864 EXPECT_EQ(rfh2, contents()->GetMainFrame());
1865 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL);
1866 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh2->rfh_state());
1867 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT, rfh1->rfh_state());
1868 EXPECT_TRUE(
1869 rfh1->frame_tree_node()->render_manager()->IsPendingDeletion(rfh1));
1871 // Simulate the swap out ack.
1872 rfh1->OnSwappedOut();
1874 // rfh1 should have been deleted.
1875 EXPECT_TRUE(rfh_deleted_observer.deleted());
1876 rfh1 = NULL;
1879 // Tests that the RenderFrameHost is properly swapped out when the SwapOut ACK
1880 // is received. (SwapOut and the corresponding ACK always occur after commit.)
1881 TEST_F(RenderFrameHostManagerTest, SwapOutFrameAfterSwapOutACK) {
1882 const GURL kUrl1("http://www.google.com/");
1883 const GURL kUrl2("http://www.chromium.org/");
1885 // Navigate to the first page.
1886 contents()->NavigateAndCommit(kUrl1);
1887 TestRenderFrameHost* rfh1 = contents()->GetMainFrame();
1888 RenderFrameDeletedObserver rfh_deleted_observer(rfh1);
1889 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh1->rfh_state());
1891 // Increment the number of active frames in SiteInstanceImpl so that rfh1 is
1892 // not deleted on swap out.
1893 rfh1->GetSiteInstance()->increment_active_frame_count();
1895 // Navigate to new site, simulating onbeforeunload approval.
1896 controller().LoadURL(
1897 kUrl2, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
1898 int entry_id = controller().GetPendingEntry()->GetUniqueID();
1899 contents()->GetMainFrame()->PrepareForCommit();
1900 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1901 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh1->rfh_state());
1902 TestRenderFrameHost* rfh2 = contents()->GetPendingMainFrame();
1904 // The new page commits.
1905 contents()->TestDidNavigate(rfh2, 1, entry_id, true, kUrl2,
1906 ui::PAGE_TRANSITION_TYPED);
1907 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1908 EXPECT_EQ(rfh2, contents()->GetMainFrame());
1909 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL);
1910 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh2->rfh_state());
1911 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT, rfh1->rfh_state());
1913 // Simulate the swap out ack.
1914 rfh1->OnSwappedOut();
1916 // rfh1 should be swapped out or deleted in --site-per-process.
1917 if (!RenderFrameHostManager::IsSwappedOutStateForbidden()) {
1918 EXPECT_FALSE(rfh_deleted_observer.deleted());
1919 EXPECT_TRUE(rfh1->is_swapped_out());
1920 } else {
1921 EXPECT_TRUE(rfh_deleted_observer.deleted());
1925 // Test that the RenderViewHost is properly swapped out if a navigation in the
1926 // new renderer commits before sending the SwapOut message to the old renderer.
1927 // This simulates a cross-site navigation to a synchronously committing URL
1928 // (e.g., a data URL) and ensures it works properly.
1929 TEST_F(RenderFrameHostManagerTest,
1930 CommitNewNavigationBeforeSendingSwapOut) {
1931 const GURL kUrl1("http://www.google.com/");
1932 const GURL kUrl2("http://www.chromium.org/");
1934 // Navigate to the first page.
1935 contents()->NavigateAndCommit(kUrl1);
1936 TestRenderFrameHost* rfh1 = contents()->GetMainFrame();
1937 RenderFrameDeletedObserver rfh_deleted_observer(rfh1);
1938 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh1->rfh_state());
1940 // Increment the number of active frames in SiteInstanceImpl so that rfh1 is
1941 // not deleted on swap out.
1942 scoped_refptr<SiteInstanceImpl> site_instance = rfh1->GetSiteInstance();
1943 site_instance->increment_active_frame_count();
1945 // Navigate to new site, simulating onbeforeunload approval.
1946 controller().LoadURL(
1947 kUrl2, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
1948 int entry_id = controller().GetPendingEntry()->GetUniqueID();
1949 rfh1->PrepareForCommit();
1950 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1951 TestRenderFrameHost* rfh2 = contents()->GetPendingMainFrame();
1953 // The new page commits.
1954 contents()->TestDidNavigate(rfh2, 1, entry_id, true, kUrl2,
1955 ui::PAGE_TRANSITION_TYPED);
1956 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1957 EXPECT_EQ(rfh2, contents()->GetMainFrame());
1958 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL);
1959 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh2->rfh_state());
1960 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT, rfh1->rfh_state());
1962 // Simulate the swap out ack.
1963 rfh1->OnSwappedOut();
1965 // rfh1 should be swapped out.
1966 if (RenderFrameHostManager::IsSwappedOutStateForbidden()) {
1967 EXPECT_TRUE(rfh_deleted_observer.deleted());
1968 EXPECT_TRUE(contents()->GetFrameTree()->root()->render_manager()
1969 ->GetRenderFrameProxyHost(site_instance.get()));
1970 } else {
1971 EXPECT_FALSE(rfh_deleted_observer.deleted());
1972 EXPECT_TRUE(rfh1->is_swapped_out());
1976 // Test that a RenderFrameHost is properly deleted when a cross-site navigation
1977 // is cancelled.
1978 TEST_F(RenderFrameHostManagerTest,
1979 CancelPendingProperlyDeletesOrSwaps) {
1980 const GURL kUrl1("http://www.google.com/");
1981 const GURL kUrl2("http://www.chromium.org/");
1982 RenderFrameHostImpl* pending_rfh = NULL;
1983 base::TimeTicks now = base::TimeTicks::Now();
1985 // Navigate to the first page.
1986 contents()->NavigateAndCommit(kUrl1);
1987 TestRenderFrameHost* rfh1 = main_test_rfh();
1988 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh1->rfh_state());
1990 // Navigate to a new site, starting a cross-site navigation.
1991 controller().LoadURL(
1992 kUrl2, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
1994 pending_rfh = contents()->GetPendingMainFrame();
1995 RenderFrameDeletedObserver rfh_deleted_observer(pending_rfh);
1997 // Cancel the navigation by simulating a declined beforeunload dialog.
1998 contents()->GetMainFrame()->OnMessageReceived(
1999 FrameHostMsg_BeforeUnload_ACK(0, false, now, now));
2000 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
2002 // Since the pending RFH is the only one for the new SiteInstance, it should
2003 // be deleted.
2004 EXPECT_TRUE(rfh_deleted_observer.deleted());
2007 // Start another cross-site navigation.
2008 controller().LoadURL(
2009 kUrl2, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
2011 pending_rfh = contents()->GetPendingMainFrame();
2012 RenderFrameDeletedObserver rfh_deleted_observer(pending_rfh);
2014 // Increment the number of active frames in the new SiteInstance, which will
2015 // cause the pending RFH to be deleted and a RenderFrameProxyHost to be
2016 // created.
2017 scoped_refptr<SiteInstanceImpl> site_instance =
2018 pending_rfh->GetSiteInstance();
2019 site_instance->increment_active_frame_count();
2021 contents()->GetMainFrame()->OnMessageReceived(
2022 FrameHostMsg_BeforeUnload_ACK(0, false, now, now));
2023 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
2025 if (RenderFrameHostManager::IsSwappedOutStateForbidden()) {
2026 EXPECT_TRUE(rfh_deleted_observer.deleted());
2027 EXPECT_TRUE(contents()->GetFrameTree()->root()->render_manager()
2028 ->GetRenderFrameProxyHost(site_instance.get()));
2029 } else {
2030 EXPECT_FALSE(rfh_deleted_observer.deleted());
2035 // Test that a pending RenderFrameHost in a non-root frame tree node is properly
2036 // deleted when the node is detached. Motivated by http://crbug.com/441357 and
2037 // http://crbug.com/444955.
2038 TEST_F(RenderFrameHostManagerTest, DetachPendingChild) {
2039 IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
2041 const GURL kUrlA("http://www.google.com/");
2042 const GURL kUrlB("http://webkit.org/");
2044 // Create a page with two child frames.
2045 contents()->NavigateAndCommit(kUrlA);
2046 contents()->GetMainFrame()->OnCreateChildFrame(
2047 contents()->GetMainFrame()->GetProcess()->GetNextRoutingID(),
2048 blink::WebTreeScopeType::Document, "frame_name",
2049 blink::WebSandboxFlags::None);
2050 contents()->GetMainFrame()->OnCreateChildFrame(
2051 contents()->GetMainFrame()->GetProcess()->GetNextRoutingID(),
2052 blink::WebTreeScopeType::Document, "frame_name",
2053 blink::WebSandboxFlags::None);
2054 RenderFrameHostManager* root_manager =
2055 contents()->GetFrameTree()->root()->render_manager();
2056 RenderFrameHostManager* iframe1 =
2057 contents()->GetFrameTree()->root()->child_at(0)->render_manager();
2058 RenderFrameHostManager* iframe2 =
2059 contents()->GetFrameTree()->root()->child_at(1)->render_manager();
2061 // 1) The first navigation.
2062 NavigationEntryImpl entryA(NULL /* instance */, -1 /* page_id */, kUrlA,
2063 Referrer(), base::string16() /* title */,
2064 ui::PAGE_TRANSITION_TYPED,
2065 false /* is_renderer_init */);
2066 RenderFrameHostImpl* host1 = NavigateToEntry(iframe1, entryA);
2068 // The RenderFrameHost created in Init will be reused.
2069 EXPECT_TRUE(host1 == iframe1->current_frame_host());
2070 EXPECT_FALSE(GetPendingFrameHost(iframe1));
2072 // Commit.
2073 iframe1->DidNavigateFrame(host1, true);
2074 // Commit to SiteInstance should be delayed until RenderFrame commit.
2075 EXPECT_TRUE(host1 == iframe1->current_frame_host());
2076 ASSERT_TRUE(host1);
2077 EXPECT_TRUE(host1->GetSiteInstance()->HasSite());
2079 // 2) Cross-site navigate both frames to next site.
2080 NavigationEntryImpl entryB(NULL /* instance */, -1 /* page_id */, kUrlB,
2081 Referrer(kUrlA, blink::WebReferrerPolicyDefault),
2082 base::string16() /* title */,
2083 ui::PAGE_TRANSITION_LINK,
2084 false /* is_renderer_init */);
2085 host1 = NavigateToEntry(iframe1, entryB);
2086 RenderFrameHostImpl* host2 = NavigateToEntry(iframe2, entryB);
2088 // A new, pending RenderFrameHost should be created in each FrameTreeNode.
2089 EXPECT_TRUE(GetPendingFrameHost(iframe1));
2090 EXPECT_TRUE(GetPendingFrameHost(iframe2));
2091 EXPECT_EQ(host1, GetPendingFrameHost(iframe1));
2092 EXPECT_EQ(host2, GetPendingFrameHost(iframe2));
2093 EXPECT_TRUE(RenderFrameHostImpl::IsRFHStateActive(
2094 GetPendingFrameHost(iframe1)->rfh_state()));
2095 EXPECT_TRUE(RenderFrameHostImpl::IsRFHStateActive(
2096 GetPendingFrameHost(iframe2)->rfh_state()));
2097 EXPECT_NE(GetPendingFrameHost(iframe1), GetPendingFrameHost(iframe2));
2098 EXPECT_EQ(GetPendingFrameHost(iframe1)->GetSiteInstance(),
2099 GetPendingFrameHost(iframe2)->GetSiteInstance());
2100 EXPECT_NE(iframe1->current_frame_host(), GetPendingFrameHost(iframe1));
2101 EXPECT_NE(iframe2->current_frame_host(), GetPendingFrameHost(iframe2));
2102 EXPECT_FALSE(contents()->CrossProcessNavigationPending())
2103 << "There should be no top-level pending navigation.";
2105 RenderFrameDeletedObserver delete_watcher1(GetPendingFrameHost(iframe1));
2106 RenderFrameDeletedObserver delete_watcher2(GetPendingFrameHost(iframe2));
2107 EXPECT_FALSE(delete_watcher1.deleted());
2108 EXPECT_FALSE(delete_watcher2.deleted());
2110 // Keep the SiteInstance alive for testing.
2111 scoped_refptr<SiteInstanceImpl> site_instance =
2112 GetPendingFrameHost(iframe1)->GetSiteInstance();
2113 EXPECT_TRUE(site_instance->HasSite());
2114 EXPECT_NE(site_instance, contents()->GetSiteInstance());
2115 EXPECT_EQ(2U, site_instance->active_frame_count());
2117 // Proxies should exist.
2118 EXPECT_NE(nullptr,
2119 root_manager->GetRenderFrameProxyHost(site_instance.get()));
2120 EXPECT_NE(nullptr,
2121 iframe1->GetRenderFrameProxyHost(site_instance.get()));
2122 EXPECT_NE(nullptr,
2123 iframe2->GetRenderFrameProxyHost(site_instance.get()));
2125 // Detach the first child FrameTreeNode. This should kill the pending host but
2126 // not yet destroy proxies in |site_instance| since the other child remains.
2127 iframe1->current_frame_host()->OnMessageReceived(
2128 FrameHostMsg_Detach(iframe1->current_frame_host()->GetRoutingID()));
2129 iframe1 = NULL; // Was just destroyed.
2131 EXPECT_TRUE(delete_watcher1.deleted());
2132 EXPECT_FALSE(delete_watcher2.deleted());
2133 EXPECT_EQ(1U, site_instance->active_frame_count());
2135 // Proxies should still exist.
2136 EXPECT_NE(nullptr,
2137 root_manager->GetRenderFrameProxyHost(site_instance.get()));
2138 EXPECT_NE(nullptr,
2139 iframe2->GetRenderFrameProxyHost(site_instance.get()));
2141 // Detach the second child FrameTreeNode. This should trigger cleanup of
2142 // RenderFrameProxyHosts in |site_instance|.
2143 iframe2->current_frame_host()->OnMessageReceived(
2144 FrameHostMsg_Detach(iframe2->current_frame_host()->GetRoutingID()));
2145 iframe2 = NULL; // Was just destroyed.
2147 EXPECT_TRUE(delete_watcher1.deleted());
2148 EXPECT_TRUE(delete_watcher2.deleted());
2150 EXPECT_EQ(0U, site_instance->active_frame_count());
2151 EXPECT_EQ(nullptr,
2152 root_manager->GetRenderFrameProxyHost(site_instance.get()))
2153 << "Proxies should have been cleaned up";
2154 EXPECT_TRUE(site_instance->HasOneRef())
2155 << "This SiteInstance should be destroyable now.";
2158 // Two tabs in the same process crash. The first tab is reloaded, and the second
2159 // tab navigates away without reloading. The second tab's navigation shouldn't
2160 // mess with the first tab's content. Motivated by http://crbug.com/473714.
2161 TEST_F(RenderFrameHostManagerTest, TwoTabsCrashOneReloadsOneLeaves) {
2162 IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
2164 const GURL kUrl1("http://www.google.com/");
2165 const GURL kUrl2("http://webkit.org/");
2166 const GURL kUrl3("http://whatwg.org/");
2168 // |contents1| and |contents2| navigate to the same page and then crash.
2169 TestWebContents* contents1 = contents();
2170 scoped_ptr<TestWebContents> contents2(
2171 TestWebContents::Create(browser_context(), contents1->GetSiteInstance()));
2172 contents1->NavigateAndCommit(kUrl1);
2173 contents2->NavigateAndCommit(kUrl1);
2174 MockRenderProcessHost* rph = contents1->GetMainFrame()->GetProcess();
2175 EXPECT_EQ(rph, contents2->GetMainFrame()->GetProcess());
2176 rph->SimulateCrash();
2177 EXPECT_FALSE(contents1->GetMainFrame()->IsRenderFrameLive());
2178 EXPECT_FALSE(contents2->GetMainFrame()->IsRenderFrameLive());
2179 EXPECT_EQ(contents1->GetSiteInstance(), contents2->GetSiteInstance());
2181 // Reload |contents1|.
2182 contents1->NavigateAndCommit(kUrl1);
2183 EXPECT_TRUE(contents1->GetMainFrame()->IsRenderFrameLive());
2184 EXPECT_FALSE(contents2->GetMainFrame()->IsRenderFrameLive());
2185 EXPECT_EQ(contents1->GetSiteInstance(), contents2->GetSiteInstance());
2187 // |contents1| creates an out of process iframe.
2188 contents1->GetMainFrame()->OnCreateChildFrame(
2189 contents1->GetMainFrame()->GetProcess()->GetNextRoutingID(),
2190 blink::WebTreeScopeType::Document, "frame_name",
2191 blink::WebSandboxFlags::None);
2192 RenderFrameHostManager* iframe =
2193 contents()->GetFrameTree()->root()->child_at(0)->render_manager();
2194 NavigationEntryImpl entry(NULL /* instance */, -1 /* page_id */, kUrl2,
2195 Referrer(kUrl1, blink::WebReferrerPolicyDefault),
2196 base::string16() /* title */,
2197 ui::PAGE_TRANSITION_LINK,
2198 false /* is_renderer_init */);
2199 RenderFrameHostImpl* cross_site = NavigateToEntry(iframe, entry);
2200 iframe->DidNavigateFrame(cross_site, true);
2202 // A proxy to the iframe should now exist in the SiteInstance of the main
2203 // frames.
2204 EXPECT_NE(cross_site->GetSiteInstance(), contents1->GetSiteInstance());
2205 EXPECT_NE(nullptr,
2206 iframe->GetRenderFrameProxyHost(contents1->GetSiteInstance()));
2207 EXPECT_NE(nullptr,
2208 iframe->GetRenderFrameProxyHost(contents2->GetSiteInstance()));
2210 // Navigate |contents2| away from the sad tab (and thus away from the
2211 // SiteInstance of |contents1|). This should not destroy the proxies needed by
2212 // |contents1| -- that was http://crbug.com/473714.
2213 EXPECT_FALSE(contents2->GetMainFrame()->IsRenderFrameLive());
2214 contents2->NavigateAndCommit(kUrl3);
2215 EXPECT_TRUE(contents2->GetMainFrame()->IsRenderFrameLive());
2216 EXPECT_NE(nullptr,
2217 iframe->GetRenderFrameProxyHost(contents1->GetSiteInstance()));
2218 EXPECT_EQ(nullptr,
2219 iframe->GetRenderFrameProxyHost(contents2->GetSiteInstance()));
2222 } // namespace content