Cast: Skip receiver log messages with time delta that can't be encoded.
[chromium-blink-merge.git] / content / browser / frame_host / render_frame_host_manager_unittest.cc
blob7a04a67246e1b9da61a7be0690186de72786361f
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/strings/utf_string_conversions.h"
6 #include "content/browser/frame_host/cross_site_transferring_request.h"
7 #include "content/browser/frame_host/navigation_controller_impl.h"
8 #include "content/browser/frame_host/navigation_entry_impl.h"
9 #include "content/browser/frame_host/navigator.h"
10 #include "content/browser/frame_host/render_frame_host_manager.h"
11 #include "content/browser/site_instance_impl.h"
12 #include "content/browser/webui/web_ui_controller_factory_registry.h"
13 #include "content/common/frame_messages.h"
14 #include "content/common/view_messages.h"
15 #include "content/public/browser/notification_details.h"
16 #include "content/public/browser/notification_service.h"
17 #include "content/public/browser/notification_source.h"
18 #include "content/public/browser/notification_types.h"
19 #include "content/public/browser/render_process_host.h"
20 #include "content/public/browser/render_widget_host_iterator.h"
21 #include "content/public/browser/web_contents_delegate.h"
22 #include "content/public/browser/web_contents_observer.h"
23 #include "content/public/browser/web_ui_controller.h"
24 #include "content/public/common/bindings_policy.h"
25 #include "content/public/common/javascript_message_type.h"
26 #include "content/public/common/page_transition_types.h"
27 #include "content/public/common/url_constants.h"
28 #include "content/public/common/url_utils.h"
29 #include "content/public/test/mock_render_process_host.h"
30 #include "content/public/test/test_notification_tracker.h"
31 #include "content/test/test_content_browser_client.h"
32 #include "content/test/test_content_client.h"
33 #include "content/test/test_render_view_host.h"
34 #include "content/test/test_web_contents.h"
35 #include "testing/gtest/include/gtest/gtest.h"
37 namespace content {
38 namespace {
40 class RenderFrameHostManagerTestWebUIControllerFactory
41 : public WebUIControllerFactory {
42 public:
43 RenderFrameHostManagerTestWebUIControllerFactory()
44 : should_create_webui_(false) {
46 virtual ~RenderFrameHostManagerTestWebUIControllerFactory() {}
48 void set_should_create_webui(bool should_create_webui) {
49 should_create_webui_ = should_create_webui;
52 // WebUIFactory implementation.
53 virtual WebUIController* CreateWebUIControllerForURL(
54 WebUI* web_ui, const GURL& url) const OVERRIDE {
55 if (!(should_create_webui_ && HasWebUIScheme(url)))
56 return NULL;
57 return new WebUIController(web_ui);
60 virtual WebUI::TypeID GetWebUIType(BrowserContext* browser_context,
61 const GURL& url) const OVERRIDE {
62 return WebUI::kNoWebUI;
65 virtual bool UseWebUIForURL(BrowserContext* browser_context,
66 const GURL& url) const OVERRIDE {
67 return HasWebUIScheme(url);
70 virtual bool UseWebUIBindingsForURL(BrowserContext* browser_context,
71 const GURL& url) const OVERRIDE {
72 return HasWebUIScheme(url);
75 private:
76 bool should_create_webui_;
78 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostManagerTestWebUIControllerFactory);
81 class BeforeUnloadFiredWebContentsDelegate : public WebContentsDelegate {
82 public:
83 BeforeUnloadFiredWebContentsDelegate() {}
84 virtual ~BeforeUnloadFiredWebContentsDelegate() {}
86 virtual void BeforeUnloadFired(WebContents* web_contents,
87 bool proceed,
88 bool* proceed_to_fire_unload) OVERRIDE {
89 *proceed_to_fire_unload = proceed;
92 private:
93 DISALLOW_COPY_AND_ASSIGN(BeforeUnloadFiredWebContentsDelegate);
96 } // namespace
98 class RenderFrameHostManagerTest
99 : public RenderViewHostImplTestHarness {
100 public:
101 virtual void SetUp() OVERRIDE {
102 RenderViewHostImplTestHarness::SetUp();
103 WebUIControllerFactory::RegisterFactory(&factory_);
106 virtual void TearDown() OVERRIDE {
107 RenderViewHostImplTestHarness::TearDown();
108 WebUIControllerFactory::UnregisterFactoryForTesting(&factory_);
111 void set_should_create_webui(bool should_create_webui) {
112 factory_.set_should_create_webui(should_create_webui);
115 void NavigateActiveAndCommit(const GURL& url) {
116 // Note: we navigate the active RenderViewHost because previous navigations
117 // won't have committed yet, so NavigateAndCommit does the wrong thing
118 // for us.
119 controller().LoadURL(url, Referrer(), PAGE_TRANSITION_LINK, std::string());
120 TestRenderViewHost* old_rvh = test_rvh();
122 // Simulate the ShouldClose_ACK that is received from the current renderer
123 // for a cross-site navigation.
124 if (old_rvh != active_rvh())
125 old_rvh->SendShouldCloseACK(true);
127 // Commit the navigation with a new page ID.
128 int32 max_page_id = contents()->GetMaxPageIDForSiteInstance(
129 active_rvh()->GetSiteInstance());
131 // Simulate the SwapOut_ACK that fires if you commit a cross-site
132 // navigation.
133 if (old_rvh != active_rvh())
134 old_rvh->OnSwappedOut(false);
136 active_test_rvh()->SendNavigate(max_page_id + 1, url);
139 bool ShouldSwapProcesses(RenderFrameHostManager* manager,
140 const NavigationEntryImpl* current_entry,
141 const NavigationEntryImpl* new_entry) const {
142 return manager->ShouldSwapBrowsingInstancesForNavigation(current_entry,
143 new_entry);
146 // Creates a test RenderViewHost that's swapped out.
147 TestRenderViewHost* CreateSwappedOutRenderViewHost() {
148 const GURL kChromeURL("chrome://foo");
149 const GURL kDestUrl("http://www.google.com/");
151 // Navigate our first tab to a chrome url and then to the destination.
152 NavigateActiveAndCommit(kChromeURL);
153 TestRenderViewHost* ntp_rvh = static_cast<TestRenderViewHost*>(
154 contents()->GetRenderManagerForTesting()->current_host());
156 // Navigate to a cross-site URL.
157 contents()->GetController().LoadURL(
158 kDestUrl, Referrer(), PAGE_TRANSITION_LINK, std::string());
159 EXPECT_TRUE(contents()->cross_navigation_pending());
161 // Manually increase the number of active views in the
162 // SiteInstance that ntp_rvh belongs to, to prevent it from being
163 // destroyed when it gets swapped out.
164 static_cast<SiteInstanceImpl*>(ntp_rvh->GetSiteInstance())->
165 increment_active_view_count();
167 TestRenderViewHost* dest_rvh = static_cast<TestRenderViewHost*>(
168 contents()->GetRenderManagerForTesting()->pending_render_view_host());
169 CHECK(dest_rvh);
170 EXPECT_NE(ntp_rvh, dest_rvh);
172 // BeforeUnload finishes.
173 ntp_rvh->SendShouldCloseACK(true);
175 dest_rvh->SendNavigate(101, kDestUrl);
176 ntp_rvh->OnSwappedOut(false);
178 EXPECT_TRUE(ntp_rvh->IsSwappedOut());
179 return ntp_rvh;
182 private:
183 RenderFrameHostManagerTestWebUIControllerFactory factory_;
186 // Tests that when you navigate from a chrome:// url to another page, and
187 // then do that same thing in another tab, that the two resulting pages have
188 // different SiteInstances, BrowsingInstances, and RenderProcessHosts. This is
189 // a regression test for bug 9364.
190 TEST_F(RenderFrameHostManagerTest, NewTabPageProcesses) {
191 set_should_create_webui(true);
192 const GURL kChromeUrl("chrome://foo");
193 const GURL kDestUrl("http://www.google.com/");
195 // Navigate our first tab to the chrome url and then to the destination,
196 // ensuring we grant bindings to the chrome URL.
197 NavigateActiveAndCommit(kChromeUrl);
198 EXPECT_TRUE(active_rvh()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
199 NavigateActiveAndCommit(kDestUrl);
201 // Make a second tab.
202 scoped_ptr<TestWebContents> contents2(
203 TestWebContents::Create(browser_context(), NULL));
205 // Load the two URLs in the second tab. Note that the first navigation creates
206 // a RVH that's not pending (since there is no cross-site transition), so
207 // we use the committed one.
208 contents2->GetController().LoadURL(
209 kChromeUrl, Referrer(), PAGE_TRANSITION_LINK, std::string());
210 TestRenderViewHost* ntp_rvh2 = static_cast<TestRenderViewHost*>(
211 contents2->GetRenderManagerForTesting()->current_host());
212 EXPECT_FALSE(contents2->cross_navigation_pending());
213 ntp_rvh2->SendNavigate(100, kChromeUrl);
215 // The second one is the opposite, creating a cross-site transition and
216 // requiring a beforeunload ack.
217 contents2->GetController().LoadURL(
218 kDestUrl, Referrer(), PAGE_TRANSITION_LINK, std::string());
219 EXPECT_TRUE(contents2->cross_navigation_pending());
220 TestRenderViewHost* dest_rvh2 = static_cast<TestRenderViewHost*>(
221 contents2->GetRenderManagerForTesting()->pending_render_view_host());
222 ASSERT_TRUE(dest_rvh2);
224 ntp_rvh2->SendShouldCloseACK(true);
225 ntp_rvh2->OnSwappedOut(false);
226 dest_rvh2->SendNavigate(101, kDestUrl);
228 // The two RVH's should be different in every way.
229 EXPECT_NE(active_rvh()->GetProcess(), dest_rvh2->GetProcess());
230 EXPECT_NE(active_rvh()->GetSiteInstance(), dest_rvh2->GetSiteInstance());
231 EXPECT_FALSE(active_rvh()->GetSiteInstance()->IsRelatedSiteInstance(
232 dest_rvh2->GetSiteInstance()));
234 // Navigate both to the new tab page, and verify that they share a
235 // RenderProcessHost (not a SiteInstance).
236 NavigateActiveAndCommit(kChromeUrl);
238 contents2->GetController().LoadURL(
239 kChromeUrl, Referrer(), PAGE_TRANSITION_LINK, std::string());
240 dest_rvh2->SendShouldCloseACK(true);
241 dest_rvh2->OnSwappedOut(false);
242 static_cast<TestRenderViewHost*>(contents2->GetRenderManagerForTesting()->
243 pending_render_view_host())->SendNavigate(102, kChromeUrl);
245 EXPECT_NE(active_rvh()->GetSiteInstance(),
246 contents2->GetRenderViewHost()->GetSiteInstance());
247 EXPECT_EQ(active_rvh()->GetSiteInstance()->GetProcess(),
248 contents2->GetRenderViewHost()->GetSiteInstance()->GetProcess());
251 // Ensure that the browser ignores most IPC messages that arrive from a
252 // RenderViewHost that has been swapped out. We do not want to take
253 // action on requests from a non-active renderer. The main exception is
254 // for synchronous messages, which cannot be ignored without leaving the
255 // renderer in a stuck state. See http://crbug.com/93427.
256 TEST_F(RenderFrameHostManagerTest, FilterMessagesWhileSwappedOut) {
257 const GURL kChromeURL("chrome://foo");
258 const GURL kDestUrl("http://www.google.com/");
260 // Navigate our first tab to a chrome url and then to the destination.
261 NavigateActiveAndCommit(kChromeURL);
262 TestRenderViewHost* ntp_rvh = static_cast<TestRenderViewHost*>(
263 contents()->GetRenderManagerForTesting()->current_host());
265 // Send an update title message and make sure it works.
266 const base::string16 ntp_title = base::ASCIIToUTF16("NTP Title");
267 blink::WebTextDirection direction = blink::WebTextDirectionLeftToRight;
268 EXPECT_TRUE(ntp_rvh->OnMessageReceived(
269 ViewHostMsg_UpdateTitle(rvh()->GetRoutingID(), 0, ntp_title, direction)));
270 EXPECT_EQ(ntp_title, contents()->GetTitle());
272 // Navigate to a cross-site URL.
273 contents()->GetController().LoadURL(
274 kDestUrl, Referrer(), PAGE_TRANSITION_LINK, std::string());
275 EXPECT_TRUE(contents()->cross_navigation_pending());
276 TestRenderViewHost* dest_rvh = static_cast<TestRenderViewHost*>(
277 contents()->GetRenderManagerForTesting()->pending_render_view_host());
278 ASSERT_TRUE(dest_rvh);
279 EXPECT_NE(ntp_rvh, dest_rvh);
281 // Create one more view in the same SiteInstance where dest_rvh2
282 // exists so that it doesn't get deleted on navigation to another
283 // site.
284 static_cast<SiteInstanceImpl*>(ntp_rvh->GetSiteInstance())->
285 increment_active_view_count();
287 // BeforeUnload finishes.
288 ntp_rvh->SendShouldCloseACK(true);
290 // Assume SwapOutACK times out, so the dest_rvh proceeds and commits.
291 dest_rvh->SendNavigate(101, kDestUrl);
293 // The new RVH should be able to update its title.
294 const base::string16 dest_title = base::ASCIIToUTF16("Google");
295 EXPECT_TRUE(dest_rvh->OnMessageReceived(
296 ViewHostMsg_UpdateTitle(rvh()->GetRoutingID(), 101, dest_title,
297 direction)));
298 EXPECT_EQ(dest_title, contents()->GetTitle());
300 // The old renderer, being slow, now updates the title. It should be filtered
301 // out and not take effect.
302 EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT, ntp_rvh->rvh_state());
303 EXPECT_TRUE(ntp_rvh->OnMessageReceived(
304 ViewHostMsg_UpdateTitle(rvh()->GetRoutingID(), 0, ntp_title, direction)));
305 EXPECT_EQ(dest_title, contents()->GetTitle());
307 // We cannot filter out synchronous IPC messages, because the renderer would
308 // be left waiting for a reply. We pick RunBeforeUnloadConfirm as an example
309 // that can run easily within a unit test, and that needs to receive a reply
310 // without showing an actual dialog.
311 MockRenderProcessHost* ntp_process_host =
312 static_cast<MockRenderProcessHost*>(ntp_rvh->GetProcess());
313 ntp_process_host->sink().ClearMessages();
314 const base::string16 msg = base::ASCIIToUTF16("Message");
315 bool result = false;
316 base::string16 unused;
317 ViewHostMsg_RunBeforeUnloadConfirm before_unload_msg(
318 rvh()->GetRoutingID(), kChromeURL, msg, false, &result, &unused);
319 // Enable pumping for check in BrowserMessageFilter::CheckCanDispatchOnUI.
320 before_unload_msg.EnableMessagePumping();
321 EXPECT_TRUE(ntp_rvh->OnMessageReceived(before_unload_msg));
322 EXPECT_TRUE(ntp_process_host->sink().GetUniqueMessageMatching(IPC_REPLY_ID));
324 // Also test RunJavaScriptMessage.
325 ntp_process_host->sink().ClearMessages();
326 ViewHostMsg_RunJavaScriptMessage js_msg(
327 rvh()->GetRoutingID(), msg, msg, kChromeURL,
328 JAVASCRIPT_MESSAGE_TYPE_CONFIRM, &result, &unused);
329 js_msg.EnableMessagePumping();
330 EXPECT_TRUE(ntp_rvh->OnMessageReceived(js_msg));
331 EXPECT_TRUE(ntp_process_host->sink().GetUniqueMessageMatching(IPC_REPLY_ID));
334 TEST_F(RenderFrameHostManagerTest, WhiteListSwapCompositorFrame) {
335 TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost();
336 TestRenderWidgetHostView* swapped_out_rwhv =
337 static_cast<TestRenderWidgetHostView*>(swapped_out_rvh->GetView());
338 EXPECT_FALSE(swapped_out_rwhv->did_swap_compositor_frame());
340 MockRenderProcessHost* process_host =
341 static_cast<MockRenderProcessHost*>(swapped_out_rvh->GetProcess());
342 process_host->sink().ClearMessages();
344 cc::CompositorFrame frame;
345 ViewHostMsg_SwapCompositorFrame msg(rvh()->GetRoutingID(), 0, frame);
347 EXPECT_TRUE(swapped_out_rvh->OnMessageReceived(msg));
348 EXPECT_TRUE(swapped_out_rwhv->did_swap_compositor_frame());
351 TEST_F(RenderFrameHostManagerTest, WhiteListDidActivateAcceleratedCompositing) {
352 TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost();
354 MockRenderProcessHost* process_host =
355 static_cast<MockRenderProcessHost*>(swapped_out_rvh->GetProcess());
356 process_host->sink().ClearMessages();
357 ViewHostMsg_DidActivateAcceleratedCompositing msg(
358 rvh()->GetRoutingID(), true);
359 EXPECT_TRUE(swapped_out_rvh->OnMessageReceived(msg));
360 EXPECT_TRUE(swapped_out_rvh->is_accelerated_compositing_active());
363 // Test if RenderViewHost::GetRenderWidgetHosts() only returns active
364 // widgets.
365 TEST_F(RenderFrameHostManagerTest, GetRenderWidgetHostsReturnsActiveViews) {
366 TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost();
367 EXPECT_TRUE(swapped_out_rvh->IsSwappedOut());
369 scoped_ptr<RenderWidgetHostIterator> widgets(
370 RenderWidgetHost::GetRenderWidgetHosts());
371 // We know that there is the only one active widget. Another view is
372 // now swapped out, so the swapped out view is not included in the
373 // list.
374 RenderWidgetHost* widget = widgets->GetNextHost();
375 EXPECT_FALSE(widgets->GetNextHost());
376 RenderViewHost* rvh = RenderViewHost::From(widget);
377 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT,
378 static_cast<RenderViewHostImpl*>(rvh)->rvh_state());
381 // Test if RenderViewHost::GetRenderWidgetHosts() returns a subset of
382 // RenderViewHostImpl::GetAllRenderWidgetHosts().
383 // RenderViewHost::GetRenderWidgetHosts() returns only active widgets, but
384 // RenderViewHostImpl::GetAllRenderWidgetHosts() returns everything
385 // including swapped out ones.
386 TEST_F(RenderFrameHostManagerTest,
387 GetRenderWidgetHostsWithinGetAllRenderWidgetHosts) {
388 TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost();
389 EXPECT_TRUE(swapped_out_rvh->IsSwappedOut());
391 scoped_ptr<RenderWidgetHostIterator> widgets(
392 RenderWidgetHost::GetRenderWidgetHosts());
394 while (RenderWidgetHost* w = widgets->GetNextHost()) {
395 bool found = false;
396 scoped_ptr<RenderWidgetHostIterator> all_widgets(
397 RenderWidgetHostImpl::GetAllRenderWidgetHosts());
398 while (RenderWidgetHost* widget = all_widgets->GetNextHost()) {
399 if (w == widget) {
400 found = true;
401 break;
404 EXPECT_TRUE(found);
408 // Test if SiteInstanceImpl::active_view_count() is correctly updated
409 // as views in a SiteInstance get swapped out and in.
410 TEST_F(RenderFrameHostManagerTest, ActiveViewCountWhileSwappingInandOut) {
411 const GURL kUrl1("http://www.google.com/");
412 const GURL kUrl2("http://www.chromium.org/");
414 // Navigate to an initial URL.
415 contents()->NavigateAndCommit(kUrl1);
416 TestRenderViewHost* rvh1 = test_rvh();
418 SiteInstanceImpl* instance1 =
419 static_cast<SiteInstanceImpl*>(rvh1->GetSiteInstance());
420 EXPECT_EQ(instance1->active_view_count(), 1U);
422 // Create 2 new tabs and simulate them being the opener chain for the main
423 // tab. They should be in the same SiteInstance.
424 scoped_ptr<TestWebContents> opener1(
425 TestWebContents::Create(browser_context(), instance1));
426 contents()->SetOpener(opener1.get());
428 scoped_ptr<TestWebContents> opener2(
429 TestWebContents::Create(browser_context(), instance1));
430 opener1->SetOpener(opener2.get());
432 EXPECT_EQ(instance1->active_view_count(), 3U);
434 // Navigate to a cross-site URL (different SiteInstance but same
435 // BrowsingInstance).
436 contents()->NavigateAndCommit(kUrl2);
437 TestRenderViewHost* rvh2 = test_rvh();
438 SiteInstanceImpl* instance2 =
439 static_cast<SiteInstanceImpl*>(rvh2->GetSiteInstance());
441 // rvh2 is on chromium.org which is different from google.com on
442 // which other tabs are.
443 EXPECT_EQ(instance2->active_view_count(), 1U);
445 // There are two active views on google.com now.
446 EXPECT_EQ(instance1->active_view_count(), 2U);
448 // Navigate to the original origin (google.com).
449 contents()->NavigateAndCommit(kUrl1);
451 EXPECT_EQ(instance1->active_view_count(), 3U);
454 // This deletes a WebContents when the given RVH is deleted. This is
455 // only for testing whether deleting an RVH does not cause any UaF in
456 // other parts of the system. For now, this class is only used for the
457 // next test cases to detect the bug mentioned at
458 // http://crbug.com/259859.
459 class RenderViewHostDestroyer : public WebContentsObserver {
460 public:
461 RenderViewHostDestroyer(RenderViewHost* render_view_host,
462 WebContents* web_contents)
463 : WebContentsObserver(WebContents::FromRenderViewHost(render_view_host)),
464 render_view_host_(render_view_host),
465 web_contents_(web_contents) {}
467 virtual void RenderViewDeleted(
468 RenderViewHost* render_view_host) OVERRIDE {
469 if (render_view_host == render_view_host_)
470 delete web_contents_;
473 private:
474 RenderViewHost* render_view_host_;
475 WebContents* web_contents_;
477 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestroyer);
480 // Test if ShutdownRenderViewHostsInSiteInstance() does not touch any
481 // RenderWidget that has been freed while deleting a RenderViewHost in
482 // a previous iteration. This is a regression test for
483 // http://crbug.com/259859.
484 TEST_F(RenderFrameHostManagerTest,
485 DetectUseAfterFreeInShutdownRenderViewHostsInSiteInstance) {
486 const GURL kChromeURL("chrome://newtab");
487 const GURL kUrl1("http://www.google.com");
488 const GURL kUrl2("http://www.chromium.org");
490 // Navigate our first tab to a chrome url and then to the destination.
491 NavigateActiveAndCommit(kChromeURL);
492 TestRenderViewHost* ntp_rvh = static_cast<TestRenderViewHost*>(
493 contents()->GetRenderManagerForTesting()->current_host());
495 // Create one more tab and navigate to kUrl1. web_contents is not
496 // wrapped as scoped_ptr since it intentionally deleted by destroyer
497 // below as part of this test.
498 TestWebContents* web_contents =
499 TestWebContents::Create(browser_context(), ntp_rvh->GetSiteInstance());
500 web_contents->NavigateAndCommit(kUrl1);
501 RenderViewHostDestroyer destroyer(ntp_rvh, web_contents);
503 // This causes the first tab to navigate to kUrl2, which destroys
504 // the ntp_rvh in ShutdownRenderViewHostsInSiteInstance(). When
505 // ntp_rvh is destroyed, it also destroys the RVHs in web_contents
506 // too. This can test whether
507 // SiteInstanceImpl::ShutdownRenderViewHostsInSiteInstance() can
508 // touch any object freed in this way or not while iterating through
509 // all widgets.
510 contents()->NavigateAndCommit(kUrl2);
513 // When there is an error with the specified page, renderer exits view-source
514 // mode. See WebFrameImpl::DidFail(). We check by this test that
515 // EnableViewSourceMode message is sent on every navigation regardless
516 // RenderView is being newly created or reused.
517 TEST_F(RenderFrameHostManagerTest, AlwaysSendEnableViewSourceMode) {
518 const GURL kChromeUrl("chrome://foo");
519 const GURL kUrl("view-source:http://foo");
521 // We have to navigate to some page at first since without this, the first
522 // navigation will reuse the SiteInstance created by Init(), and the second
523 // one will create a new SiteInstance. Because current_instance and
524 // new_instance will be different, a new RenderViewHost will be created for
525 // the second navigation. We have to avoid this in order to exercise the
526 // target code patch.
527 NavigateActiveAndCommit(kChromeUrl);
529 // Navigate.
530 controller().LoadURL(
531 kUrl, Referrer(), PAGE_TRANSITION_TYPED, std::string());
532 // Simulate response from RenderView for FirePageBeforeUnload.
533 base::TimeTicks now = base::TimeTicks::Now();
534 test_rvh()->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(
535 rvh()->GetRoutingID(), true, now, now));
536 ASSERT_TRUE(pending_rvh()); // New pending RenderViewHost will be created.
537 RenderViewHost* last_rvh = pending_rvh();
538 int32 new_id = contents()->GetMaxPageIDForSiteInstance(
539 active_rvh()->GetSiteInstance()) + 1;
540 pending_test_rvh()->SendNavigate(new_id, kUrl);
541 EXPECT_EQ(controller().GetLastCommittedEntryIndex(), 1);
542 ASSERT_TRUE(controller().GetLastCommittedEntry());
543 EXPECT_TRUE(kUrl == controller().GetLastCommittedEntry()->GetURL());
544 EXPECT_FALSE(controller().GetPendingEntry());
545 // Because we're using TestWebContents and TestRenderViewHost in this
546 // unittest, no one calls WebContentsImpl::RenderViewCreated(). So, we see no
547 // EnableViewSourceMode message, here.
549 // Clear queued messages before load.
550 process()->sink().ClearMessages();
551 // Navigate, again.
552 controller().LoadURL(
553 kUrl, Referrer(), PAGE_TRANSITION_TYPED, std::string());
554 // The same RenderViewHost should be reused.
555 EXPECT_FALSE(pending_rvh());
556 EXPECT_TRUE(last_rvh == rvh());
557 test_rvh()->SendNavigate(new_id, kUrl); // The same page_id returned.
558 EXPECT_EQ(controller().GetLastCommittedEntryIndex(), 1);
559 EXPECT_FALSE(controller().GetPendingEntry());
560 // New message should be sent out to make sure to enter view-source mode.
561 EXPECT_TRUE(process()->sink().GetUniqueMessageMatching(
562 ViewMsg_EnableViewSourceMode::ID));
565 // Tests the Init function by checking the initial RenderViewHost.
566 TEST_F(RenderFrameHostManagerTest, Init) {
567 // Using TestBrowserContext.
568 SiteInstanceImpl* instance =
569 static_cast<SiteInstanceImpl*>(SiteInstance::Create(browser_context()));
570 EXPECT_FALSE(instance->HasSite());
572 scoped_ptr<TestWebContents> web_contents(
573 TestWebContents::Create(browser_context(), instance));
574 FrameTree tree(web_contents->GetFrameTree()->root()->navigator(),
575 web_contents.get(), web_contents.get(),
576 web_contents.get(), web_contents.get());
577 RenderFrameHostManager* manager = tree.root()->render_manager();
579 manager->Init(browser_context(), instance, MSG_ROUTING_NONE,
580 MSG_ROUTING_NONE);
582 RenderViewHostImpl* rvh = manager->current_host();
583 RenderFrameHostImpl* rfh = manager->current_frame_host();
584 ASSERT_TRUE(rvh);
585 ASSERT_TRUE(rfh);
586 EXPECT_EQ(rvh, rfh->render_view_host());
587 EXPECT_EQ(instance, rvh->GetSiteInstance());
588 EXPECT_EQ(web_contents.get(), rvh->GetDelegate());
589 EXPECT_EQ(web_contents.get(), rfh->delegate());
590 EXPECT_TRUE(manager->GetRenderWidgetHostView());
591 EXPECT_FALSE(manager->pending_render_view_host());
594 // Tests the Navigate function. We navigate three sites consecutively and check
595 // how the pending/committed RenderViewHost are modified.
596 TEST_F(RenderFrameHostManagerTest, Navigate) {
597 TestNotificationTracker notifications;
599 SiteInstance* instance = SiteInstance::Create(browser_context());
601 scoped_ptr<TestWebContents> web_contents(
602 TestWebContents::Create(browser_context(), instance));
603 notifications.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
604 Source<WebContents>(web_contents.get()));
606 // Create.
607 FrameTree tree(web_contents->GetFrameTree()->root()->navigator(),
608 web_contents.get(), web_contents.get(),
609 web_contents.get(), web_contents.get());
610 RenderFrameHostManager* manager = tree.root()->render_manager();
612 manager->Init(browser_context(), instance, MSG_ROUTING_NONE,
613 MSG_ROUTING_NONE);
615 RenderFrameHostImpl* host;
617 // 1) The first navigation. --------------------------
618 const GURL kUrl1("http://www.google.com/");
619 NavigationEntryImpl entry1(
620 NULL /* instance */, -1 /* page_id */, kUrl1, Referrer(),
621 base::string16() /* title */, PAGE_TRANSITION_TYPED,
622 false /* is_renderer_init */);
623 host = manager->Navigate(entry1);
625 // The RenderFrameHost created in Init will be reused.
626 EXPECT_TRUE(host == manager->current_frame_host());
627 EXPECT_FALSE(manager->pending_frame_host());
629 // Commit.
630 manager->DidNavigateMainFrame(host->render_view_host());
631 // Commit to SiteInstance should be delayed until RenderView commit.
632 EXPECT_TRUE(host == manager->current_frame_host());
633 ASSERT_TRUE(host);
634 EXPECT_FALSE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
635 HasSite());
636 static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->SetSite(kUrl1);
638 // 2) Navigate to next site. -------------------------
639 const GURL kUrl2("http://www.google.com/foo");
640 NavigationEntryImpl entry2(
641 NULL /* instance */, -1 /* page_id */, kUrl2,
642 Referrer(kUrl1, blink::WebReferrerPolicyDefault),
643 base::string16() /* title */, PAGE_TRANSITION_LINK,
644 true /* is_renderer_init */);
645 host = manager->Navigate(entry2);
647 // The RenderFrameHost created in Init will be reused.
648 EXPECT_TRUE(host == manager->current_frame_host());
649 EXPECT_FALSE(manager->pending_frame_host());
651 // Commit.
652 manager->DidNavigateMainFrame(host->render_view_host());
653 EXPECT_TRUE(host == manager->current_frame_host());
654 ASSERT_TRUE(host);
655 EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
656 HasSite());
658 // 3) Cross-site navigate to next site. --------------
659 const GURL kUrl3("http://webkit.org/");
660 NavigationEntryImpl entry3(
661 NULL /* instance */, -1 /* page_id */, kUrl3,
662 Referrer(kUrl2, blink::WebReferrerPolicyDefault),
663 base::string16() /* title */, PAGE_TRANSITION_LINK,
664 false /* is_renderer_init */);
665 host = manager->Navigate(entry3);
667 // A new RenderFrameHost should be created.
668 EXPECT_TRUE(manager->pending_frame_host());
669 ASSERT_EQ(host, manager->pending_frame_host());
671 notifications.Reset();
673 // Commit.
674 manager->DidNavigateMainFrame(manager->pending_render_view_host());
675 EXPECT_TRUE(host == manager->current_frame_host());
676 ASSERT_TRUE(host);
677 EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
678 HasSite());
679 // Check the pending RenderFrameHost has been committed.
680 EXPECT_FALSE(manager->pending_frame_host());
682 // We should observe a notification.
683 EXPECT_TRUE(
684 notifications.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED));
687 // Tests the Navigate function. In this unit test we verify that the Navigate
688 // function can handle a new navigation event before the previous navigation
689 // has been committed. This is also a regression test for
690 // http://crbug.com/104600.
691 TEST_F(RenderFrameHostManagerTest, NavigateWithEarlyReNavigation) {
692 TestNotificationTracker notifications;
694 SiteInstance* instance = SiteInstance::Create(browser_context());
696 scoped_ptr<TestWebContents> web_contents(
697 TestWebContents::Create(browser_context(), instance));
698 notifications.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
699 Source<WebContents>(web_contents.get()));
701 // Create.
702 FrameTree tree(web_contents->GetFrameTree()->root()->navigator(),
703 web_contents.get(), web_contents.get(),
704 web_contents.get(), web_contents.get());
705 RenderFrameHostManager* manager = tree.root()->render_manager();
707 manager->Init(browser_context(), instance, MSG_ROUTING_NONE,
708 MSG_ROUTING_NONE);
710 // 1) The first navigation. --------------------------
711 const GURL kUrl1("http://www.google.com/");
712 NavigationEntryImpl entry1(NULL /* instance */, -1 /* page_id */, kUrl1,
713 Referrer(), base::string16() /* title */,
714 PAGE_TRANSITION_TYPED,
715 false /* is_renderer_init */);
716 RenderFrameHostImpl* host = manager->Navigate(entry1);
718 // The RenderFrameHost created in Init will be reused.
719 EXPECT_TRUE(host == manager->current_frame_host());
720 EXPECT_FALSE(manager->pending_frame_host());
722 // We should observe a notification.
723 EXPECT_TRUE(
724 notifications.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED));
725 notifications.Reset();
727 // Commit.
728 manager->DidNavigateMainFrame(host->render_view_host());
730 // Commit to SiteInstance should be delayed until RenderView commit.
731 EXPECT_TRUE(host == manager->current_frame_host());
732 ASSERT_TRUE(host);
733 EXPECT_FALSE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
734 HasSite());
735 static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->SetSite(kUrl1);
737 // 2) Cross-site navigate to next site. -------------------------
738 const GURL kUrl2("http://www.example.com");
739 NavigationEntryImpl entry2(
740 NULL /* instance */, -1 /* page_id */, kUrl2, Referrer(),
741 base::string16() /* title */, PAGE_TRANSITION_TYPED,
742 false /* is_renderer_init */);
743 RenderFrameHostImpl* host2 = manager->Navigate(entry2);
744 int host2_process_id = host2->GetProcess()->GetID();
746 // A new RenderFrameHost should be created.
747 EXPECT_TRUE(manager->pending_frame_host());
748 ASSERT_EQ(host2, manager->pending_frame_host());
749 EXPECT_NE(host2, host);
751 // Check that the navigation is still suspended because the old RVH
752 // is not swapped out, yet.
753 EXPECT_TRUE(host2->render_view_host()->are_navigations_suspended());
754 MockRenderProcessHost* test_process_host2 =
755 static_cast<MockRenderProcessHost*>(host2->GetProcess());
756 test_process_host2->sink().ClearMessages();
757 host2->render_view_host()->NavigateToURL(kUrl2);
758 EXPECT_FALSE(test_process_host2->sink().GetUniqueMessageMatching(
759 FrameMsg_Navigate::ID));
761 // Allow closing the current Render View (precondition for swapping out
762 // the RVH): Simulate response from RenderView for ViewMsg_ShouldClose sent by
763 // FirePageBeforeUnload.
764 TestRenderViewHost* test_host =
765 static_cast<TestRenderViewHost*>(host->render_view_host());
766 MockRenderProcessHost* test_process_host =
767 static_cast<MockRenderProcessHost*>(test_host->GetProcess());
768 EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching(
769 ViewMsg_ShouldClose::ID));
770 test_host->SendShouldCloseACK(true);
772 // CrossSiteResourceHandler::StartCrossSiteTransition triggers a
773 // call of RenderFrameHostManager::SwapOutOldPage before
774 // RenderFrameHostManager::DidNavigateMainFrame is called.
775 // The RVH is swapped out after receiving the unload ack.
776 manager->SwapOutOldPage();
777 EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching(
778 ViewMsg_SwapOut::ID));
779 test_host->OnSwappedOut(false);
781 EXPECT_EQ(host, manager->current_frame_host());
782 EXPECT_FALSE(manager->current_frame_host()->is_swapped_out());
783 EXPECT_EQ(host2, manager->pending_frame_host());
784 // There should be still no navigation messages being sent.
785 EXPECT_FALSE(test_process_host2->sink().GetUniqueMessageMatching(
786 FrameMsg_Navigate::ID));
788 // 3) Cross-site navigate to next site before 2) has committed. --------------
789 const GURL kUrl3("http://webkit.org/");
790 NavigationEntryImpl entry3(NULL /* instance */, -1 /* page_id */, kUrl3,
791 Referrer(), base::string16() /* title */,
792 PAGE_TRANSITION_TYPED,
793 false /* is_renderer_init */);
794 test_process_host->sink().ClearMessages();
795 RenderFrameHostImpl* host3 = manager->Navigate(entry3);
797 // A new RenderFrameHost should be created. host2 is now deleted.
798 EXPECT_TRUE(manager->pending_frame_host());
799 ASSERT_EQ(host3, manager->pending_frame_host());
800 EXPECT_NE(host3, host);
801 EXPECT_NE(host3->GetProcess()->GetID(), host2_process_id);
803 // Navigations in the new RVH should be suspended.
804 EXPECT_TRUE(static_cast<RenderViewHostImpl*>(
805 host3->render_view_host())->are_navigations_suspended());
806 EXPECT_EQ(host, manager->current_frame_host());
807 EXPECT_FALSE(manager->current_frame_host()->is_swapped_out());
809 // Simulate a response to the second beforeunload request.
810 EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching(
811 ViewMsg_ShouldClose::ID));
812 test_host->SendShouldCloseACK(true);
814 // CrossSiteResourceHandler::StartCrossSiteTransition triggers a
815 // call of RenderFrameHostManager::SwapOutOldPage before
816 // RenderFrameHostManager::DidNavigateMainFrame is called.
817 manager->SwapOutOldPage();
818 EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching(
819 ViewMsg_SwapOut::ID));
820 test_host->OnSwappedOut(false);
822 // Commit.
823 manager->DidNavigateMainFrame(host3->render_view_host());
824 EXPECT_TRUE(host3 == manager->current_frame_host());
825 ASSERT_TRUE(host3);
826 EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host3->GetSiteInstance())->
827 HasSite());
828 // Check the pending RenderFrameHost has been committed.
829 EXPECT_FALSE(manager->pending_frame_host());
831 // We should observe a notification.
832 EXPECT_TRUE(
833 notifications.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED));
836 // Tests WebUI creation.
837 TEST_F(RenderFrameHostManagerTest, WebUI) {
838 set_should_create_webui(true);
839 SiteInstance* instance = SiteInstance::Create(browser_context());
841 scoped_ptr<TestWebContents> web_contents(
842 TestWebContents::Create(browser_context(), instance));
843 FrameTree tree(web_contents->GetFrameTree()->root()->navigator(),
844 web_contents.get(), web_contents.get(),
845 web_contents.get(), web_contents.get());
846 RenderFrameHostManager* manager = tree.root()->render_manager();
848 manager->Init(browser_context(), instance, MSG_ROUTING_NONE,
849 MSG_ROUTING_NONE);
850 EXPECT_FALSE(manager->current_host()->IsRenderViewLive());
852 const GURL kUrl("chrome://foo");
853 NavigationEntryImpl entry(NULL /* instance */, -1 /* page_id */, kUrl,
854 Referrer(), base::string16() /* title */,
855 PAGE_TRANSITION_TYPED,
856 false /* is_renderer_init */);
857 RenderFrameHostImpl* host = manager->Navigate(entry);
859 // We commit the pending RenderFrameHost immediately because the previous
860 // RenderFrameHost was not live. We test a case where it is live in
861 // WebUIInNewTab.
862 EXPECT_TRUE(host);
863 EXPECT_EQ(host, manager->current_frame_host());
864 EXPECT_FALSE(manager->pending_frame_host());
866 // It's important that the site instance get set on the Web UI page as soon
867 // as the navigation starts, rather than lazily after it commits, so we don't
868 // try to re-use the SiteInstance/process for non Web UI things that may
869 // get loaded in between.
870 EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
871 HasSite());
872 EXPECT_EQ(kUrl, host->GetSiteInstance()->GetSiteURL());
874 // The Web UI is committed immediately because the RenderViewHost has not been
875 // used yet. UpdateRendererStateForNavigate() took the short cut path.
876 EXPECT_FALSE(manager->pending_web_ui());
877 EXPECT_TRUE(manager->web_ui());
879 // Commit.
880 manager->DidNavigateMainFrame(host->render_view_host());
881 EXPECT_TRUE(
882 host->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
885 // Tests that we can open a WebUI link in a new tab from a WebUI page and still
886 // grant the correct bindings. http://crbug.com/189101.
887 TEST_F(RenderFrameHostManagerTest, WebUIInNewTab) {
888 set_should_create_webui(true);
889 SiteInstance* blank_instance = SiteInstance::Create(browser_context());
891 // Create a blank tab.
892 scoped_ptr<TestWebContents> web_contents1(
893 TestWebContents::Create(browser_context(), blank_instance));
894 FrameTree tree1(web_contents1->GetFrameTree()->root()->navigator(),
895 web_contents1.get(), web_contents1.get(),
896 web_contents1.get(), web_contents1.get());
897 RenderFrameHostManager* manager1 = tree1.root()->render_manager();
898 manager1->Init(
899 browser_context(), blank_instance, MSG_ROUTING_NONE, MSG_ROUTING_NONE);
900 // Test the case that new RVH is considered live.
901 manager1->current_host()->CreateRenderView(base::string16(), -1, -1);
903 // Navigate to a WebUI page.
904 const GURL kUrl1("chrome://foo");
905 NavigationEntryImpl entry1(NULL /* instance */, -1 /* page_id */, kUrl1,
906 Referrer(), base::string16() /* title */,
907 PAGE_TRANSITION_TYPED,
908 false /* is_renderer_init */);
909 RenderFrameHostImpl* host1 = manager1->Navigate(entry1);
911 // We should have a pending navigation to the WebUI RenderViewHost.
912 // It should already have bindings.
913 EXPECT_EQ(host1, manager1->pending_frame_host());
914 EXPECT_NE(host1, manager1->current_frame_host());
915 EXPECT_TRUE(
916 host1->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
918 // Commit and ensure we still have bindings.
919 manager1->DidNavigateMainFrame(host1->render_view_host());
920 SiteInstance* webui_instance = host1->GetSiteInstance();
921 EXPECT_EQ(host1, manager1->current_frame_host());
922 EXPECT_TRUE(
923 host1->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
925 // Now simulate clicking a link that opens in a new tab.
926 scoped_ptr<TestWebContents> web_contents2(
927 TestWebContents::Create(browser_context(), webui_instance));
928 FrameTree tree2(web_contents2->GetFrameTree()->root()->navigator(),
929 web_contents2.get(), web_contents2.get(),
930 web_contents2.get(), web_contents2.get());
931 RenderFrameHostManager* manager2 = tree2.root()->render_manager();
932 manager2->Init(
933 browser_context(), webui_instance, MSG_ROUTING_NONE, MSG_ROUTING_NONE);
934 // Make sure the new RVH is considered live. This is usually done in
935 // RenderWidgetHost::Init when opening a new tab from a link.
936 manager2->current_host()->CreateRenderView(base::string16(), -1, -1);
938 const GURL kUrl2("chrome://foo/bar");
939 NavigationEntryImpl entry2(NULL /* instance */, -1 /* page_id */, kUrl2,
940 Referrer(), base::string16() /* title */,
941 PAGE_TRANSITION_LINK,
942 true /* is_renderer_init */);
943 RenderFrameHostImpl* host2 = manager2->Navigate(entry2);
945 // No cross-process transition happens because we are already in the right
946 // SiteInstance. We should grant bindings immediately.
947 EXPECT_EQ(host2, manager2->current_frame_host());
948 EXPECT_TRUE(
949 host2->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
951 manager2->DidNavigateMainFrame(host2->render_view_host());
954 // Tests that we don't end up in an inconsistent state if a page does a back and
955 // then reload. http://crbug.com/51680
956 TEST_F(RenderFrameHostManagerTest, PageDoesBackAndReload) {
957 const GURL kUrl1("http://www.google.com/");
958 const GURL kUrl2("http://www.evil-site.com/");
960 // Navigate to a safe site, then an evil site.
961 // This will switch RenderViewHosts. We cannot assert that the first and
962 // second RVHs are different, though, because the first one may be promptly
963 // deleted.
964 contents()->NavigateAndCommit(kUrl1);
965 contents()->NavigateAndCommit(kUrl2);
966 RenderViewHost* evil_rvh = contents()->GetRenderViewHost();
968 // Now let's simulate the evil page calling history.back().
969 contents()->OnGoToEntryAtOffset(-1);
970 // We should have a new pending RVH.
971 // Note that in this case, the navigation has not committed, so evil_rvh will
972 // not be deleted yet.
973 EXPECT_NE(evil_rvh, contents()->GetRenderManagerForTesting()->
974 pending_render_view_host());
976 // Before that RVH has committed, the evil page reloads itself.
977 FrameHostMsg_DidCommitProvisionalLoad_Params params;
978 params.page_id = 1;
979 params.url = kUrl2;
980 params.transition = PAGE_TRANSITION_CLIENT_REDIRECT;
981 params.should_update_history = false;
982 params.gesture = NavigationGestureAuto;
983 params.was_within_same_page = false;
984 params.is_post = false;
985 params.page_state = PageState::CreateFromURL(kUrl2);
987 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(evil_rvh);
988 RenderFrameHostImpl* rfh = RenderFrameHostImpl::FromID(
989 rvh->GetProcess()->GetID(), rvh->main_frame_routing_id());
990 contents()->GetFrameTree()->root()->navigator()->DidNavigate(rfh, params);
992 // That should have cancelled the pending RVH, and the evil RVH should be the
993 // current one.
994 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
995 pending_render_view_host() == NULL);
996 EXPECT_EQ(evil_rvh, contents()->GetRenderManagerForTesting()->current_host());
998 // Also we should not have a pending navigation entry.
999 EXPECT_TRUE(contents()->GetController().GetPendingEntry() == NULL);
1000 NavigationEntry* entry = contents()->GetController().GetVisibleEntry();
1001 ASSERT_TRUE(entry != NULL);
1002 EXPECT_EQ(kUrl2, entry->GetURL());
1005 // Ensure that we can go back and forward even if a SwapOut ACK isn't received.
1006 // See http://crbug.com/93427.
1007 TEST_F(RenderFrameHostManagerTest, NavigateAfterMissingSwapOutACK) {
1008 const GURL kUrl1("http://www.google.com/");
1009 const GURL kUrl2("http://www.chromium.org/");
1011 // Navigate to two pages.
1012 contents()->NavigateAndCommit(kUrl1);
1013 TestRenderViewHost* rvh1 = test_rvh();
1015 // Keep active_view_count nonzero so that no swapped out views in
1016 // this SiteInstance get forcefully deleted.
1017 static_cast<SiteInstanceImpl*>(rvh1->GetSiteInstance())->
1018 increment_active_view_count();
1020 contents()->NavigateAndCommit(kUrl2);
1021 TestRenderViewHost* rvh2 = test_rvh();
1022 static_cast<SiteInstanceImpl*>(rvh2->GetSiteInstance())->
1023 increment_active_view_count();
1025 // Now go back, but suppose the SwapOut_ACK isn't received. This shouldn't
1026 // happen, but we have seen it when going back quickly across many entries
1027 // (http://crbug.com/93427).
1028 contents()->GetController().GoBack();
1029 EXPECT_TRUE(rvh2->is_waiting_for_beforeunload_ack());
1030 contents()->ProceedWithCrossSiteNavigation();
1031 EXPECT_FALSE(rvh2->is_waiting_for_beforeunload_ack());
1032 rvh2->SwapOut();
1033 EXPECT_TRUE(rvh2->IsWaitingForUnloadACK());
1035 // The back navigation commits.
1036 const NavigationEntry* entry1 = contents()->GetController().GetPendingEntry();
1037 rvh1->SendNavigate(entry1->GetPageID(), entry1->GetURL());
1038 EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT, rvh2->rvh_state());
1040 // We should be able to navigate forward.
1041 contents()->GetController().GoForward();
1042 contents()->ProceedWithCrossSiteNavigation();
1043 const NavigationEntry* entry2 = contents()->GetController().GetPendingEntry();
1044 rvh2->SendNavigate(entry2->GetPageID(), entry2->GetURL());
1045 EXPECT_EQ(rvh2, rvh());
1046 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state());
1047 EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT, rvh1->rvh_state());
1048 rvh1->OnSwappedOut(false);
1049 EXPECT_TRUE(rvh1->IsSwappedOut());
1052 // Test that we create swapped out RVHs for the opener chain when navigating an
1053 // opened tab cross-process. This allows us to support certain cross-process
1054 // JavaScript calls (http://crbug.com/99202).
1055 TEST_F(RenderFrameHostManagerTest, CreateSwappedOutOpenerRVHs) {
1056 const GURL kUrl1("http://www.google.com/");
1057 const GURL kUrl2("http://www.chromium.org/");
1058 const GURL kChromeUrl("chrome://foo");
1060 // Navigate to an initial URL.
1061 contents()->NavigateAndCommit(kUrl1);
1062 RenderFrameHostManager* manager = contents()->GetRenderManagerForTesting();
1063 TestRenderViewHost* rvh1 = test_rvh();
1065 // Create 2 new tabs and simulate them being the opener chain for the main
1066 // tab. They should be in the same SiteInstance.
1067 scoped_ptr<TestWebContents> opener1(
1068 TestWebContents::Create(browser_context(), rvh1->GetSiteInstance()));
1069 RenderFrameHostManager* opener1_manager =
1070 opener1->GetRenderManagerForTesting();
1071 contents()->SetOpener(opener1.get());
1073 scoped_ptr<TestWebContents> opener2(
1074 TestWebContents::Create(browser_context(), rvh1->GetSiteInstance()));
1075 RenderFrameHostManager* opener2_manager =
1076 opener2->GetRenderManagerForTesting();
1077 opener1->SetOpener(opener2.get());
1079 // Navigate to a cross-site URL (different SiteInstance but same
1080 // BrowsingInstance).
1081 contents()->NavigateAndCommit(kUrl2);
1082 TestRenderViewHost* rvh2 = test_rvh();
1083 EXPECT_NE(rvh1->GetSiteInstance(), rvh2->GetSiteInstance());
1084 EXPECT_TRUE(rvh1->GetSiteInstance()->IsRelatedSiteInstance(
1085 rvh2->GetSiteInstance()));
1087 // Ensure rvh1 is placed on swapped out list of the current tab.
1088 EXPECT_TRUE(manager->IsRVHOnSwappedOutList(rvh1));
1089 EXPECT_EQ(rvh1,
1090 manager->GetSwappedOutRenderViewHost(rvh1->GetSiteInstance()));
1092 // Ensure a swapped out RVH is created in the first opener tab.
1093 TestRenderViewHost* opener1_rvh = static_cast<TestRenderViewHost*>(
1094 opener1_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance()));
1095 EXPECT_TRUE(opener1_manager->IsRVHOnSwappedOutList(opener1_rvh));
1096 EXPECT_TRUE(opener1_rvh->IsSwappedOut());
1098 // Ensure a swapped out RVH is created in the second opener tab.
1099 TestRenderViewHost* opener2_rvh = static_cast<TestRenderViewHost*>(
1100 opener2_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance()));
1101 EXPECT_TRUE(opener2_manager->IsRVHOnSwappedOutList(opener2_rvh));
1102 EXPECT_TRUE(opener2_rvh->IsSwappedOut());
1104 // Navigate to a cross-BrowsingInstance URL.
1105 contents()->NavigateAndCommit(kChromeUrl);
1106 TestRenderViewHost* rvh3 = test_rvh();
1107 EXPECT_NE(rvh1->GetSiteInstance(), rvh3->GetSiteInstance());
1108 EXPECT_FALSE(rvh1->GetSiteInstance()->IsRelatedSiteInstance(
1109 rvh3->GetSiteInstance()));
1111 // No scripting is allowed across BrowsingInstances, so we should not create
1112 // swapped out RVHs for the opener chain in this case.
1113 EXPECT_FALSE(opener1_manager->GetSwappedOutRenderViewHost(
1114 rvh3->GetSiteInstance()));
1115 EXPECT_FALSE(opener2_manager->GetSwappedOutRenderViewHost(
1116 rvh3->GetSiteInstance()));
1119 // Test that we clean up swapped out RenderViewHosts when a process hosting
1120 // those associated RenderViews crashes. http://crbug.com/258993
1121 TEST_F(RenderFrameHostManagerTest, CleanUpSwappedOutRVHOnProcessCrash) {
1122 const GURL kUrl1("http://www.google.com/");
1123 const GURL kUrl2("http://www.chromium.org/");
1125 // Navigate to an initial URL.
1126 contents()->NavigateAndCommit(kUrl1);
1127 TestRenderViewHost* rvh1 = test_rvh();
1129 // Create a new tab as an opener for the main tab.
1130 scoped_ptr<TestWebContents> opener1(
1131 TestWebContents::Create(browser_context(), rvh1->GetSiteInstance()));
1132 RenderFrameHostManager* opener1_manager =
1133 opener1->GetRenderManagerForTesting();
1134 contents()->SetOpener(opener1.get());
1136 // Make sure the new opener RVH is considered live.
1137 opener1_manager->current_host()->CreateRenderView(base::string16(), -1, -1);
1139 // Use a cross-process navigation in the opener to swap out the old RVH.
1140 EXPECT_FALSE(opener1_manager->GetSwappedOutRenderViewHost(
1141 rvh1->GetSiteInstance()));
1142 opener1->NavigateAndCommit(kUrl2);
1143 EXPECT_TRUE(opener1_manager->GetSwappedOutRenderViewHost(
1144 rvh1->GetSiteInstance()));
1146 // Fake a process crash.
1147 RenderProcessHost::RendererClosedDetails details(
1148 rvh1->GetProcess()->GetHandle(),
1149 base::TERMINATION_STATUS_PROCESS_CRASHED,
1151 NotificationService::current()->Notify(
1152 NOTIFICATION_RENDERER_PROCESS_CLOSED,
1153 Source<RenderProcessHost>(rvh1->GetProcess()),
1154 Details<RenderProcessHost::RendererClosedDetails>(&details));
1155 rvh1->set_render_view_created(false);
1157 // Ensure that the swapped out RenderViewHost has been deleted.
1158 EXPECT_FALSE(opener1_manager->GetSwappedOutRenderViewHost(
1159 rvh1->GetSiteInstance()));
1161 // Reload the initial tab. This should recreate the opener's swapped out RVH
1162 // in the original SiteInstance.
1163 contents()->GetController().Reload(true);
1164 EXPECT_EQ(opener1_manager->GetSwappedOutRenderViewHost(
1165 rvh1->GetSiteInstance())->GetRoutingID(),
1166 test_rvh()->opener_route_id());
1169 // Test that RenderViewHosts created for WebUI navigations are properly
1170 // granted WebUI bindings even if an unprivileged swapped out RenderViewHost
1171 // is in the same process (http://crbug.com/79918).
1172 TEST_F(RenderFrameHostManagerTest, EnableWebUIWithSwappedOutOpener) {
1173 set_should_create_webui(true);
1174 const GURL kSettingsUrl("chrome://chrome/settings");
1175 const GURL kPluginUrl("chrome://plugins");
1177 // Navigate to an initial WebUI URL.
1178 contents()->NavigateAndCommit(kSettingsUrl);
1180 // Ensure the RVH has WebUI bindings.
1181 TestRenderViewHost* rvh1 = test_rvh();
1182 EXPECT_TRUE(rvh1->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1184 // Create a new tab and simulate it being the opener for the main
1185 // tab. It should be in the same SiteInstance.
1186 scoped_ptr<TestWebContents> opener1(
1187 TestWebContents::Create(browser_context(), rvh1->GetSiteInstance()));
1188 RenderFrameHostManager* opener1_manager =
1189 opener1->GetRenderManagerForTesting();
1190 contents()->SetOpener(opener1.get());
1192 // Navigate to a different WebUI URL (different SiteInstance, same
1193 // BrowsingInstance).
1194 contents()->NavigateAndCommit(kPluginUrl);
1195 TestRenderViewHost* rvh2 = test_rvh();
1196 EXPECT_NE(rvh1->GetSiteInstance(), rvh2->GetSiteInstance());
1197 EXPECT_TRUE(rvh1->GetSiteInstance()->IsRelatedSiteInstance(
1198 rvh2->GetSiteInstance()));
1200 // Ensure a swapped out RVH is created in the first opener tab.
1201 TestRenderViewHost* opener1_rvh = static_cast<TestRenderViewHost*>(
1202 opener1_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance()));
1203 EXPECT_TRUE(opener1_manager->IsRVHOnSwappedOutList(opener1_rvh));
1204 EXPECT_TRUE(opener1_rvh->IsSwappedOut());
1206 // Ensure the new RVH has WebUI bindings.
1207 EXPECT_TRUE(rvh2->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1210 // Test that we reuse the same guest SiteInstance if we navigate across sites.
1211 TEST_F(RenderFrameHostManagerTest, NoSwapOnGuestNavigations) {
1212 TestNotificationTracker notifications;
1214 GURL guest_url(std::string(kGuestScheme).append("://abc123"));
1215 SiteInstance* instance =
1216 SiteInstance::CreateForURL(browser_context(), guest_url);
1217 scoped_ptr<TestWebContents> web_contents(
1218 TestWebContents::Create(browser_context(), instance));
1220 // Create.
1221 FrameTree tree(web_contents->GetFrameTree()->root()->navigator(),
1222 web_contents.get(), web_contents.get(),
1223 web_contents.get(), web_contents.get());
1224 RenderFrameHostManager* manager = tree.root()->render_manager();
1226 manager->Init(browser_context(), instance, MSG_ROUTING_NONE,
1227 MSG_ROUTING_NONE);
1229 RenderFrameHostImpl* host;
1231 // 1) The first navigation. --------------------------
1232 const GURL kUrl1("http://www.google.com/");
1233 NavigationEntryImpl entry1(
1234 NULL /* instance */, -1 /* page_id */, kUrl1, Referrer(),
1235 base::string16() /* title */, PAGE_TRANSITION_TYPED,
1236 false /* is_renderer_init */);
1237 host = manager->Navigate(entry1);
1239 // The RenderFrameHost created in Init will be reused.
1240 EXPECT_TRUE(host == manager->current_frame_host());
1241 EXPECT_FALSE(manager->pending_frame_host());
1242 EXPECT_EQ(manager->current_frame_host()->GetSiteInstance(), instance);
1244 // Commit.
1245 manager->DidNavigateMainFrame(host->render_view_host());
1246 // Commit to SiteInstance should be delayed until RenderView commit.
1247 EXPECT_EQ(host, manager->current_frame_host());
1248 ASSERT_TRUE(host);
1249 EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
1250 HasSite());
1252 // 2) Navigate to a different domain. -------------------------
1253 // Guests stay in the same process on navigation.
1254 const GURL kUrl2("http://www.chromium.org");
1255 NavigationEntryImpl entry2(
1256 NULL /* instance */, -1 /* page_id */, kUrl2,
1257 Referrer(kUrl1, blink::WebReferrerPolicyDefault),
1258 base::string16() /* title */, PAGE_TRANSITION_LINK,
1259 true /* is_renderer_init */);
1260 host = manager->Navigate(entry2);
1262 // The RenderFrameHost created in Init will be reused.
1263 EXPECT_EQ(host, manager->current_frame_host());
1264 EXPECT_FALSE(manager->pending_frame_host());
1266 // Commit.
1267 manager->DidNavigateMainFrame(host->render_view_host());
1268 EXPECT_EQ(host, manager->current_frame_host());
1269 ASSERT_TRUE(host);
1270 EXPECT_EQ(static_cast<SiteInstanceImpl*>(host->GetSiteInstance()),
1271 instance);
1274 // Test that we cancel a pending RVH if we close the tab while it's pending.
1275 // http://crbug.com/294697.
1276 TEST_F(RenderFrameHostManagerTest, NavigateWithEarlyClose) {
1277 TestNotificationTracker notifications;
1279 SiteInstance* instance = SiteInstance::Create(browser_context());
1281 BeforeUnloadFiredWebContentsDelegate delegate;
1282 scoped_ptr<TestWebContents> web_contents(
1283 TestWebContents::Create(browser_context(), instance));
1284 web_contents->SetDelegate(&delegate);
1285 notifications.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
1286 Source<WebContents>(web_contents.get()));
1288 // Create.
1289 FrameTree tree(web_contents->GetFrameTree()->root()->navigator(),
1290 web_contents.get(), web_contents.get(),
1291 web_contents.get(), web_contents.get());
1292 RenderFrameHostManager* manager = tree.root()->render_manager();
1294 manager->Init(browser_context(), instance, MSG_ROUTING_NONE,
1295 MSG_ROUTING_NONE);
1297 // 1) The first navigation. --------------------------
1298 const GURL kUrl1("http://www.google.com/");
1299 NavigationEntryImpl entry1(NULL /* instance */, -1 /* page_id */, kUrl1,
1300 Referrer(), base::string16() /* title */,
1301 PAGE_TRANSITION_TYPED,
1302 false /* is_renderer_init */);
1303 RenderFrameHostImpl* host = manager->Navigate(entry1);
1305 // The RenderFrameHost created in Init will be reused.
1306 EXPECT_EQ(host, manager->current_frame_host());
1307 EXPECT_FALSE(manager->pending_frame_host());
1309 // We should observe a notification.
1310 EXPECT_TRUE(
1311 notifications.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED));
1312 notifications.Reset();
1314 // Commit.
1315 manager->DidNavigateMainFrame(host->render_view_host());
1317 // Commit to SiteInstance should be delayed until RenderFrame commits.
1318 EXPECT_EQ(host, manager->current_frame_host());
1319 EXPECT_FALSE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
1320 HasSite());
1321 static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->SetSite(kUrl1);
1323 // 2) Cross-site navigate to next site. -------------------------
1324 const GURL kUrl2("http://www.example.com");
1325 NavigationEntryImpl entry2(
1326 NULL /* instance */, -1 /* page_id */, kUrl2, Referrer(),
1327 base::string16() /* title */, PAGE_TRANSITION_TYPED,
1328 false /* is_renderer_init */);
1329 RenderFrameHostImpl* host2 = manager->Navigate(entry2);
1331 // A new RenderFrameHost should be created.
1332 ASSERT_EQ(host2, manager->pending_frame_host());
1333 EXPECT_NE(host2, host);
1335 EXPECT_EQ(host, manager->current_frame_host());
1336 EXPECT_FALSE(manager->current_frame_host()->is_swapped_out());
1337 EXPECT_EQ(host2, manager->pending_frame_host());
1339 // 3) Close the tab. -------------------------
1340 notifications.ListenFor(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
1341 Source<RenderWidgetHost>(host2->render_view_host()));
1342 manager->ShouldClosePage(false, true, base::TimeTicks());
1344 EXPECT_TRUE(
1345 notifications.Check1AndReset(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED));
1346 EXPECT_FALSE(manager->pending_frame_host());
1347 EXPECT_EQ(host, manager->current_frame_host());
1350 // This checks that the given RVH has been properly deleted.
1351 class RenderViewHostDestructionObserver : public WebContentsObserver {
1352 public:
1353 RenderViewHostDestructionObserver(RenderViewHost* render_view_host)
1354 : WebContentsObserver(WebContents::FromRenderViewHost(render_view_host)),
1355 render_view_host_(render_view_host),
1356 rvh_deleted_(false) {}
1358 bool rvh_deleted() { return rvh_deleted_; }
1360 virtual void RenderViewDeleted(RenderViewHost* render_view_host) OVERRIDE {
1361 if (render_view_host == render_view_host_)
1362 rvh_deleted_ = true;
1365 private:
1366 RenderViewHost* render_view_host_;
1367 bool rvh_deleted_;
1369 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestructionObserver);
1372 // Tests that the RenderViewHost is properly deleted when the SwapOutACK is
1373 // received before the new page commits.
1374 TEST_F(RenderFrameHostManagerTest,
1375 SwapOutACKBeforeNewPageCommitsLeadsToDeletion) {
1376 const GURL kUrl1("http://www.google.com/");
1377 const GURL kUrl2("http://www.chromium.org/");
1379 // Navigate to the first page.
1380 contents()->NavigateAndCommit(kUrl1);
1381 TestRenderViewHost* rvh1 = test_rvh();
1382 RenderViewHostDestructionObserver destruction_observer(rvh1);
1383 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
1385 // Navigate to new site, simulating onbeforeunload approval.
1386 controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
1387 base::TimeTicks now = base::TimeTicks::Now();
1388 rvh1->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
1389 EXPECT_TRUE(contents()->cross_navigation_pending());
1390 TestRenderViewHost* rvh2 =
1391 static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
1393 // Simulate rvh2's response, which leads to an unload request being sent to
1394 // rvh1.
1395 std::vector<GURL> url_chain;
1396 url_chain.push_back(GURL());
1397 contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
1398 contents()->GetRenderManagerForTesting()->pending_frame_host(),
1399 GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
1400 url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
1401 EXPECT_TRUE(contents()->cross_navigation_pending());
1402 EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK,
1403 rvh1->rvh_state());
1405 // Simulate the swap out ack.
1406 rvh1->OnSwappedOut(false);
1407 EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT, rvh1->rvh_state());
1409 // The new page commits.
1410 contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED);
1411 EXPECT_FALSE(contents()->cross_navigation_pending());
1412 EXPECT_EQ(rvh2, rvh());
1413 EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
1414 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state());
1416 // rvh1 should have been deleted.
1417 EXPECT_TRUE(destruction_observer.rvh_deleted());
1418 rvh1 = NULL;
1421 // Tests that the RenderViewHost is properly swapped out when the SwapOutACK is
1422 // received before the new page commits.
1423 TEST_F(RenderFrameHostManagerTest,
1424 SwapOutACKBeforeNewPageCommitsLeadsToSwapOut) {
1425 const GURL kUrl1("http://www.google.com/");
1426 const GURL kUrl2("http://www.chromium.org/");
1428 // Navigate to the first page.
1429 contents()->NavigateAndCommit(kUrl1);
1430 TestRenderViewHost* rvh1 = test_rvh();
1431 RenderViewHostDestructionObserver destruction_observer(rvh1);
1432 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
1434 // Increment the number of active views in SiteInstanceImpl so that rvh2 is
1435 // not deleted on swap out.
1436 static_cast<SiteInstanceImpl*>(
1437 rvh1->GetSiteInstance())->increment_active_view_count();
1439 // Navigate to new site, simulating onbeforeunload approval.
1440 controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
1441 base::TimeTicks now = base::TimeTicks::Now();
1442 rvh1->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
1443 EXPECT_TRUE(contents()->cross_navigation_pending());
1444 TestRenderViewHost* rvh2 =
1445 static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
1447 // Simulate rvh2's response, which leads to an unload request being sent to
1448 // rvh1.
1449 std::vector<GURL> url_chain;
1450 url_chain.push_back(GURL());
1451 contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
1452 contents()->GetRenderManagerForTesting()->pending_frame_host(),
1453 GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
1454 url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
1455 EXPECT_TRUE(contents()->cross_navigation_pending());
1456 EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK,
1457 rvh1->rvh_state());
1459 // Simulate the swap out ack.
1460 rvh1->OnSwappedOut(false);
1461 EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT, rvh1->rvh_state());
1463 // The new page commits.
1464 contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED);
1465 EXPECT_FALSE(contents()->cross_navigation_pending());
1466 EXPECT_EQ(rvh2, rvh());
1467 EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
1468 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state());
1470 // rvh1 should be swapped out.
1471 EXPECT_FALSE(destruction_observer.rvh_deleted());
1472 EXPECT_TRUE(rvh1->IsSwappedOut());
1475 // Tests that the RenderViewHost is properly deleted when the new
1476 // page commits before the swap out ack is received.
1477 TEST_F(RenderFrameHostManagerTest,
1478 NewPageCommitsBeforeSwapOutACKLeadsToDeletion) {
1479 const GURL kUrl1("http://www.google.com/");
1480 const GURL kUrl2("http://www.chromium.org/");
1482 // Navigate to the first page.
1483 contents()->NavigateAndCommit(kUrl1);
1484 TestRenderViewHost* rvh1 = test_rvh();
1485 RenderViewHostDestructionObserver destruction_observer(rvh1);
1486 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
1488 // Navigate to new site, simulating onbeforeunload approval.
1489 controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
1490 base::TimeTicks now = base::TimeTicks::Now();
1491 rvh1->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
1492 EXPECT_TRUE(contents()->cross_navigation_pending());
1493 TestRenderViewHost* rvh2 =
1494 static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
1496 // Simulate rvh2's response, which leads to an unload request being sent to
1497 // rvh1.
1498 std::vector<GURL> url_chain;
1499 url_chain.push_back(GURL());
1500 contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
1501 contents()->GetRenderManagerForTesting()->pending_frame_host(),
1502 GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
1503 url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
1504 EXPECT_TRUE(contents()->cross_navigation_pending());
1505 EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK,
1506 rvh1->rvh_state());
1508 // The new page commits.
1509 contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED);
1510 EXPECT_FALSE(contents()->cross_navigation_pending());
1511 EXPECT_EQ(rvh2, rvh());
1512 EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
1513 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state());
1514 EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SHUTDOWN, rvh1->rvh_state());
1516 // Simulate the swap out ack.
1517 rvh1->OnSwappedOut(false);
1519 // rvh1 should have been deleted.
1520 EXPECT_TRUE(destruction_observer.rvh_deleted());
1521 rvh1 = NULL;
1524 // Tests that the RenderViewHost is properly swapped out when the new page
1525 // commits before the swap out ack is received.
1526 TEST_F(RenderFrameHostManagerTest,
1527 NewPageCommitsBeforeSwapOutACKLeadsToSwapOut) {
1528 const GURL kUrl1("http://www.google.com/");
1529 const GURL kUrl2("http://www.chromium.org/");
1531 // Navigate to the first page.
1532 contents()->NavigateAndCommit(kUrl1);
1533 TestRenderViewHost* rvh1 = test_rvh();
1534 RenderViewHostDestructionObserver destruction_observer(rvh1);
1535 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
1537 // Increment the number of active views in SiteInstanceImpl so that rvh1 is
1538 // not deleted on swap out.
1539 static_cast<SiteInstanceImpl*>(
1540 rvh1->GetSiteInstance())->increment_active_view_count();
1542 // Navigate to new site, simulating onbeforeunload approval.
1543 controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
1544 base::TimeTicks now = base::TimeTicks::Now();
1545 rvh1->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
1546 EXPECT_TRUE(contents()->cross_navigation_pending());
1547 TestRenderViewHost* rvh2 =
1548 static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
1550 // Simulate rvh2's response, which leads to an unload request being sent to
1551 // rvh1.
1552 std::vector<GURL> url_chain;
1553 url_chain.push_back(GURL());
1554 contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
1555 contents()->GetRenderManagerForTesting()->pending_frame_host(),
1556 GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
1557 url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
1558 EXPECT_TRUE(contents()->cross_navigation_pending());
1559 EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK,
1560 rvh1->rvh_state());
1562 // The new page commits.
1563 contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED);
1564 EXPECT_FALSE(contents()->cross_navigation_pending());
1565 EXPECT_EQ(rvh2, rvh());
1566 EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
1567 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state());
1568 EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT, rvh1->rvh_state());
1570 // Simulate the swap out ack.
1571 rvh1->OnSwappedOut(false);
1573 // rvh1 should be swapped out.
1574 EXPECT_FALSE(destruction_observer.rvh_deleted());
1575 EXPECT_TRUE(rvh1->IsSwappedOut());
1578 } // namespace content