render_frame_host_manager_unittest.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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.
4
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"
36
37namespace content {
38namespace {
39
40class RenderFrameHostManagerTestWebUIControllerFactory
41    : public WebUIControllerFactory {
42 public:
43  RenderFrameHostManagerTestWebUIControllerFactory()
44    : should_create_webui_(false) {
45  }
46  virtual ~RenderFrameHostManagerTestWebUIControllerFactory() {}
47
48  void set_should_create_webui(bool should_create_webui) {
49    should_create_webui_ = should_create_webui;
50  }
51
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);
58  }
59
60   virtual WebUI::TypeID GetWebUIType(BrowserContext* browser_context,
61      const GURL& url) const OVERRIDE {
62    return WebUI::kNoWebUI;
63  }
64
65  virtual bool UseWebUIForURL(BrowserContext* browser_context,
66                              const GURL& url) const OVERRIDE {
67    return HasWebUIScheme(url);
68  }
69
70  virtual bool UseWebUIBindingsForURL(BrowserContext* browser_context,
71                                      const GURL& url) const OVERRIDE {
72    return HasWebUIScheme(url);
73  }
74
75 private:
76  bool should_create_webui_;
77
78  DISALLOW_COPY_AND_ASSIGN(RenderFrameHostManagerTestWebUIControllerFactory);
79};
80
81class BeforeUnloadFiredWebContentsDelegate : public WebContentsDelegate {
82 public:
83  BeforeUnloadFiredWebContentsDelegate() {}
84  virtual ~BeforeUnloadFiredWebContentsDelegate() {}
85
86  virtual void BeforeUnloadFired(WebContents* web_contents,
87                                 bool proceed,
88                                 bool* proceed_to_fire_unload) OVERRIDE {
89    *proceed_to_fire_unload = proceed;
90  }
91
92 private:
93  DISALLOW_COPY_AND_ASSIGN(BeforeUnloadFiredWebContentsDelegate);
94};
95
96}  // namespace
97
98class RenderFrameHostManagerTest
99    : public RenderViewHostImplTestHarness {
100 public:
101  virtual void SetUp() OVERRIDE {
102    RenderViewHostImplTestHarness::SetUp();
103    WebUIControllerFactory::RegisterFactory(&factory_);
104  }
105
106  virtual void TearDown() OVERRIDE {
107    RenderViewHostImplTestHarness::TearDown();
108    WebUIControllerFactory::UnregisterFactoryForTesting(&factory_);
109  }
110
111  void set_should_create_webui(bool should_create_webui) {
112    factory_.set_should_create_webui(should_create_webui);
113  }
114
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();
121
122    // Simulate the BeforeUnload_ACK that is received from the current renderer
123    // for a cross-site navigation.
124    if (old_rvh != active_rvh())
125      old_rvh->SendBeforeUnloadACK(true);
126
127    // Commit the navigation with a new page ID.
128    int32 max_page_id = contents()->GetMaxPageIDForSiteInstance(
129        active_rvh()->GetSiteInstance());
130
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);
135
136    active_test_rvh()->SendNavigate(max_page_id + 1, url);
137  }
138
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);
144  }
145
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/");
150
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());
155
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());
160
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();
166
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);
171
172    // BeforeUnload finishes.
173    ntp_rvh->SendBeforeUnloadACK(true);
174
175    dest_rvh->SendNavigate(101, kDestUrl);
176    ntp_rvh->OnSwappedOut(false);
177
178    EXPECT_TRUE(ntp_rvh->IsSwappedOut());
179    return ntp_rvh;
180  }
181
182 private:
183  RenderFrameHostManagerTestWebUIControllerFactory factory_;
184};
185
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.
190TEST_F(RenderFrameHostManagerTest, NewTabPageProcesses) {
191  set_should_create_webui(true);
192  const GURL kChromeUrl("chrome://foo");
193  const GURL kDestUrl("http://www.google.com/");
194
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);
200
201  // Make a second tab.
202  scoped_ptr<TestWebContents> contents2(
203      TestWebContents::Create(browser_context(), NULL));
204
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);
214
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);
223
224  ntp_rvh2->SendBeforeUnloadACK(true);
225  ntp_rvh2->OnSwappedOut(false);
226  dest_rvh2->SendNavigate(101, kDestUrl);
227
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()));
233
234  // Navigate both to the new tab page, and verify that they share a
235  // RenderProcessHost (not a SiteInstance).
236  NavigateActiveAndCommit(kChromeUrl);
237
238  contents2->GetController().LoadURL(
239      kChromeUrl, Referrer(), PAGE_TRANSITION_LINK, std::string());
240  dest_rvh2->SendBeforeUnloadACK(true);
241  dest_rvh2->OnSwappedOut(false);
242  static_cast<TestRenderViewHost*>(contents2->GetRenderManagerForTesting()->
243     pending_render_view_host())->SendNavigate(102, kChromeUrl);
244
245  EXPECT_NE(active_rvh()->GetSiteInstance(),
246            contents2->GetRenderViewHost()->GetSiteInstance());
247  EXPECT_EQ(active_rvh()->GetSiteInstance()->GetProcess(),
248            contents2->GetRenderViewHost()->GetSiteInstance()->GetProcess());
249}
250
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.
256TEST_F(RenderFrameHostManagerTest, FilterMessagesWhileSwappedOut) {
257  const GURL kChromeURL("chrome://foo");
258  const GURL kDestUrl("http://www.google.com/");
259
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());
264
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());
271
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);
280
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();
286
287  // BeforeUnload finishes.
288  ntp_rvh->SendBeforeUnloadACK(true);
289
290  // Assume SwapOutACK times out, so the dest_rvh proceeds and commits.
291  dest_rvh->SendNavigate(101, kDestUrl);
292
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());
299
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());
306
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));
323
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));
332}
333
334TEST_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());
339
340  MockRenderProcessHost* process_host =
341      static_cast<MockRenderProcessHost*>(swapped_out_rvh->GetProcess());
342  process_host->sink().ClearMessages();
343
344  cc::CompositorFrame frame;
345  ViewHostMsg_SwapCompositorFrame msg(rvh()->GetRoutingID(), 0, frame);
346
347  EXPECT_TRUE(swapped_out_rvh->OnMessageReceived(msg));
348  EXPECT_TRUE(swapped_out_rwhv->did_swap_compositor_frame());
349}
350
351TEST_F(RenderFrameHostManagerTest, WhiteListDidActivateAcceleratedCompositing) {
352  TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost();
353
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());
361}
362
363// Test if RenderViewHost::GetRenderWidgetHosts() only returns active
364// widgets.
365TEST_F(RenderFrameHostManagerTest, GetRenderWidgetHostsReturnsActiveViews) {
366  TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost();
367  EXPECT_TRUE(swapped_out_rvh->IsSwappedOut());
368
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());
379}
380
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.
386TEST_F(RenderFrameHostManagerTest,
387       GetRenderWidgetHostsWithinGetAllRenderWidgetHosts) {
388  TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost();
389  EXPECT_TRUE(swapped_out_rvh->IsSwappedOut());
390
391  scoped_ptr<RenderWidgetHostIterator> widgets(
392      RenderWidgetHost::GetRenderWidgetHosts());
393
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;
402      }
403    }
404    EXPECT_TRUE(found);
405  }
406}
407
408// Test if SiteInstanceImpl::active_view_count() is correctly updated
409// as views in a SiteInstance get swapped out and in.
410TEST_F(RenderFrameHostManagerTest, ActiveViewCountWhileSwappingInandOut) {
411  const GURL kUrl1("http://www.google.com/");
412  const GURL kUrl2("http://www.chromium.org/");
413
414  // Navigate to an initial URL.
415  contents()->NavigateAndCommit(kUrl1);
416  TestRenderViewHost* rvh1 = test_rvh();
417
418  SiteInstanceImpl* instance1 =
419      static_cast<SiteInstanceImpl*>(rvh1->GetSiteInstance());
420  EXPECT_EQ(instance1->active_view_count(), 1U);
421
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());
427
428  scoped_ptr<TestWebContents> opener2(
429      TestWebContents::Create(browser_context(), instance1));
430  opener1->SetOpener(opener2.get());
431
432  EXPECT_EQ(instance1->active_view_count(), 3U);
433
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());
440
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);
444
445  // There are two active views on google.com now.
446  EXPECT_EQ(instance1->active_view_count(), 2U);
447
448  // Navigate to the original origin (google.com).
449  contents()->NavigateAndCommit(kUrl1);
450
451  EXPECT_EQ(instance1->active_view_count(), 3U);
452}
453
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.
459class 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) {}
466
467  virtual void RenderViewDeleted(
468      RenderViewHost* render_view_host) OVERRIDE {
469    if (render_view_host == render_view_host_)
470      delete web_contents_;
471  }
472
473 private:
474  RenderViewHost* render_view_host_;
475  WebContents* web_contents_;
476
477  DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestroyer);
478};
479
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.
484TEST_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");
489
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());
494
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);
502
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);
511}
512
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.
517TEST_F(RenderFrameHostManagerTest, AlwaysSendEnableViewSourceMode) {
518  const GURL kChromeUrl("chrome://foo");
519  const GURL kUrl("view-source:http://foo");
520
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);
528
529  // Navigate.
530  controller().LoadURL(
531      kUrl, Referrer(), PAGE_TRANSITION_TYPED, std::string());
532  // Simulate response from RenderFrame for DispatchBeforeUnload.
533  base::TimeTicks now = base::TimeTicks::Now();
534  main_test_rfh()->OnMessageReceived(FrameHostMsg_BeforeUnload_ACK(
535      main_test_rfh()->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.
548
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));
563}
564
565// Tests the Init function by checking the initial RenderViewHost.
566TEST_F(RenderFrameHostManagerTest, Init) {
567  // Using TestBrowserContext.
568  SiteInstanceImpl* instance =
569      static_cast<SiteInstanceImpl*>(SiteInstance::Create(browser_context()));
570  EXPECT_FALSE(instance->HasSite());
571
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();
578
579  manager->Init(browser_context(), instance, MSG_ROUTING_NONE,
580                MSG_ROUTING_NONE);
581
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());
592}
593
594// Tests the Navigate function. We navigate three sites consecutively and check
595// how the pending/committed RenderViewHost are modified.
596TEST_F(RenderFrameHostManagerTest, Navigate) {
597  TestNotificationTracker notifications;
598
599  SiteInstance* instance = SiteInstance::Create(browser_context());
600
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()));
605
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();
611
612  manager->Init(browser_context(), instance, MSG_ROUTING_NONE,
613                MSG_ROUTING_NONE);
614
615  RenderFrameHostImpl* host;
616
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);
624
625  // The RenderFrameHost created in Init will be reused.
626  EXPECT_TRUE(host == manager->current_frame_host());
627  EXPECT_FALSE(manager->pending_frame_host());
628
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);
637
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);
646
647  // The RenderFrameHost created in Init will be reused.
648  EXPECT_TRUE(host == manager->current_frame_host());
649  EXPECT_FALSE(manager->pending_frame_host());
650
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());
657
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);
666
667  // A new RenderFrameHost should be created.
668  EXPECT_TRUE(manager->pending_frame_host());
669  ASSERT_EQ(host, manager->pending_frame_host());
670
671  notifications.Reset();
672
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());
681
682  // We should observe a notification.
683  EXPECT_TRUE(
684      notifications.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED));
685}
686
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.
691TEST_F(RenderFrameHostManagerTest, NavigateWithEarlyReNavigation) {
692  TestNotificationTracker notifications;
693
694  SiteInstance* instance = SiteInstance::Create(browser_context());
695
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()));
700
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();
706
707  manager->Init(browser_context(), instance, MSG_ROUTING_NONE,
708                MSG_ROUTING_NONE);
709
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);
717
718  // The RenderFrameHost created in Init will be reused.
719  EXPECT_TRUE(host == manager->current_frame_host());
720  EXPECT_FALSE(manager->pending_frame_host());
721
722  // We should observe a notification.
723  EXPECT_TRUE(
724      notifications.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED));
725  notifications.Reset();
726
727  // Commit.
728  manager->DidNavigateMainFrame(host->render_view_host());
729
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);
736
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();
745
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);
750
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));
760
761  // Allow closing the current Render View (precondition for swapping out
762  // the RVH): Simulate response from RenderFrame for FrameMsg_BeforeUnload sent
763  // by DispatchBeforeUnload.
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      FrameMsg_BeforeUnload::ID));
770  test_host->SendBeforeUnloadACK(true);
771
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);
780
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));
787
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);
796
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);
802
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());
808
809  // Simulate a response to the second beforeunload request.
810  EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching(
811      FrameMsg_BeforeUnload::ID));
812  test_host->SendBeforeUnloadACK(true);
813
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);
821
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());
830
831  // We should observe a notification.
832  EXPECT_TRUE(
833      notifications.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED));
834}
835
836// Tests WebUI creation.
837TEST_F(RenderFrameHostManagerTest, WebUI) {
838  set_should_create_webui(true);
839  SiteInstance* instance = SiteInstance::Create(browser_context());
840
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();
847
848  manager->Init(browser_context(), instance, MSG_ROUTING_NONE,
849                MSG_ROUTING_NONE);
850  EXPECT_FALSE(manager->current_host()->IsRenderViewLive());
851
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);
858
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());
865
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());
873
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());
878
879  // Commit.
880  manager->DidNavigateMainFrame(host->render_view_host());
881  EXPECT_TRUE(
882      host->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
883}
884
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.
887TEST_F(RenderFrameHostManagerTest, WebUIInNewTab) {
888  set_should_create_webui(true);
889  SiteInstance* blank_instance = SiteInstance::Create(browser_context());
890
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);
902
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);
910
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);
917
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);
924
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);
937
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);
944
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);
950
951  manager2->DidNavigateMainFrame(host2->render_view_host());
952}
953
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
956TEST_F(RenderFrameHostManagerTest, PageDoesBackAndReload) {
957  const GURL kUrl1("http://www.google.com/");
958  const GURL kUrl2("http://www.evil-site.com/");
959
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();
967
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());
975
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);
986
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);
991
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());
997
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());
1003}
1004
1005// Ensure that we can go back and forward even if a SwapOut ACK isn't received.
1006// See http://crbug.com/93427.
1007TEST_F(RenderFrameHostManagerTest, NavigateAfterMissingSwapOutACK) {
1008  const GURL kUrl1("http://www.google.com/");
1009  const GURL kUrl2("http://www.chromium.org/");
1010
1011  // Navigate to two pages.
1012  contents()->NavigateAndCommit(kUrl1);
1013  TestRenderViewHost* rvh1 = test_rvh();
1014
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();
1019
1020  contents()->NavigateAndCommit(kUrl2);
1021  TestRenderViewHost* rvh2 = test_rvh();
1022  static_cast<SiteInstanceImpl*>(rvh2->GetSiteInstance())->
1023      increment_active_view_count();
1024
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());
1034
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());
1039
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());
1050}
1051
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).
1055TEST_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");
1059
1060  // Navigate to an initial URL.
1061  contents()->NavigateAndCommit(kUrl1);
1062  RenderFrameHostManager* manager = contents()->GetRenderManagerForTesting();
1063  TestRenderViewHost* rvh1 = test_rvh();
1064
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());
1072
1073  scoped_ptr<TestWebContents> opener2(
1074      TestWebContents::Create(browser_context(), rvh1->GetSiteInstance()));
1075  RenderFrameHostManager* opener2_manager =
1076      opener2->GetRenderManagerForTesting();
1077  opener1->SetOpener(opener2.get());
1078
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()));
1086
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()));
1091
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());
1097
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());
1103
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()));
1110
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()));
1117}
1118
1119// Test that we clean up swapped out RenderViewHosts when a process hosting
1120// those associated RenderViews crashes. http://crbug.com/258993
1121TEST_F(RenderFrameHostManagerTest, CleanUpSwappedOutRVHOnProcessCrash) {
1122  const GURL kUrl1("http://www.google.com/");
1123  const GURL kUrl2("http://www.chromium.org/");
1124
1125  // Navigate to an initial URL.
1126  contents()->NavigateAndCommit(kUrl1);
1127  TestRenderViewHost* rvh1 = test_rvh();
1128
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());
1135
1136  // Make sure the new opener RVH is considered live.
1137  opener1_manager->current_host()->CreateRenderView(base::string16(), -1, -1);
1138
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()));
1145
1146  // Fake a process crash.
1147  RenderProcessHost::RendererClosedDetails details(
1148      rvh1->GetProcess()->GetHandle(),
1149      base::TERMINATION_STATUS_PROCESS_CRASHED,
1150      0);
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);
1156
1157  // Ensure that the swapped out RenderViewHost has been deleted.
1158  EXPECT_FALSE(opener1_manager->GetSwappedOutRenderViewHost(
1159      rvh1->GetSiteInstance()));
1160
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());
1167}
1168
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).
1172TEST_F(RenderFrameHostManagerTest, EnableWebUIWithSwappedOutOpener) {
1173  set_should_create_webui(true);
1174  const GURL kSettingsUrl("chrome://chrome/settings");
1175  const GURL kPluginUrl("chrome://plugins");
1176
1177  // Navigate to an initial WebUI URL.
1178  contents()->NavigateAndCommit(kSettingsUrl);
1179
1180  // Ensure the RVH has WebUI bindings.
1181  TestRenderViewHost* rvh1 = test_rvh();
1182  EXPECT_TRUE(rvh1->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1183
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());
1191
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()));
1199
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());
1205
1206  // Ensure the new RVH has WebUI bindings.
1207  EXPECT_TRUE(rvh2->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1208}
1209
1210// Test that we reuse the same guest SiteInstance if we navigate across sites.
1211TEST_F(RenderFrameHostManagerTest, NoSwapOnGuestNavigations) {
1212  TestNotificationTracker notifications;
1213
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));
1219
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();
1225
1226  manager->Init(browser_context(), instance, MSG_ROUTING_NONE,
1227                MSG_ROUTING_NONE);
1228
1229  RenderFrameHostImpl* host;
1230
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);
1238
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);
1243
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());
1251
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);
1261
1262  // The RenderFrameHost created in Init will be reused.
1263  EXPECT_EQ(host, manager->current_frame_host());
1264  EXPECT_FALSE(manager->pending_frame_host());
1265
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);
1272}
1273
1274// Test that we cancel a pending RVH if we close the tab while it's pending.
1275// http://crbug.com/294697.
1276TEST_F(RenderFrameHostManagerTest, NavigateWithEarlyClose) {
1277  TestNotificationTracker notifications;
1278
1279  SiteInstance* instance = SiteInstance::Create(browser_context());
1280
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()));
1287
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();
1293
1294  manager->Init(browser_context(), instance, MSG_ROUTING_NONE,
1295                MSG_ROUTING_NONE);
1296
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);
1304
1305  // The RenderFrameHost created in Init will be reused.
1306  EXPECT_EQ(host, manager->current_frame_host());
1307  EXPECT_FALSE(manager->pending_frame_host());
1308
1309  // We should observe a notification.
1310  EXPECT_TRUE(
1311      notifications.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED));
1312  notifications.Reset();
1313
1314  // Commit.
1315  manager->DidNavigateMainFrame(host->render_view_host());
1316
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);
1322
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);
1330
1331  // A new RenderFrameHost should be created.
1332  ASSERT_EQ(host2, manager->pending_frame_host());
1333  EXPECT_NE(host2, host);
1334
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());
1338
1339  // 3) Close the tab. -------------------------
1340  notifications.ListenFor(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
1341                          Source<RenderWidgetHost>(host2->render_view_host()));
1342  manager->OnBeforeUnloadACK(false, true, base::TimeTicks());
1343
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());
1348}
1349
1350// This checks that the given RVH has been properly deleted.
1351class 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) {}
1357
1358  bool rvh_deleted() { return rvh_deleted_; }
1359
1360  virtual void RenderViewDeleted(RenderViewHost* render_view_host) OVERRIDE {
1361    if (render_view_host == render_view_host_)
1362      rvh_deleted_ = true;
1363  }
1364
1365 private:
1366  RenderViewHost* render_view_host_;
1367  bool rvh_deleted_;
1368
1369  DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestructionObserver);
1370};
1371
1372// Tests that the RenderViewHost is properly deleted when the SwapOutACK is
1373// received before the new page commits.
1374TEST_F(RenderFrameHostManagerTest,
1375       SwapOutACKBeforeNewPageCommitsLeadsToDeletion) {
1376  const GURL kUrl1("http://www.google.com/");
1377  const GURL kUrl2("http://www.chromium.org/");
1378
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());
1384
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  main_test_rfh()->OnMessageReceived(
1389      FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
1390  EXPECT_TRUE(contents()->cross_navigation_pending());
1391  TestRenderViewHost* rvh2 =
1392      static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
1393
1394  // Simulate rvh2's response, which leads to an unload request being sent to
1395  // rvh1.
1396  std::vector<GURL> url_chain;
1397  url_chain.push_back(GURL());
1398  contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
1399      contents()->GetRenderManagerForTesting()->pending_frame_host(),
1400      GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
1401      url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
1402  EXPECT_TRUE(contents()->cross_navigation_pending());
1403  EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK,
1404            rvh1->rvh_state());
1405
1406  // Simulate the swap out ack.
1407  rvh1->OnSwappedOut(false);
1408  EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT, rvh1->rvh_state());
1409
1410  // The new page commits.
1411  contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED);
1412  EXPECT_FALSE(contents()->cross_navigation_pending());
1413  EXPECT_EQ(rvh2, rvh());
1414  EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
1415  EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state());
1416
1417  // rvh1 should have been deleted.
1418  EXPECT_TRUE(destruction_observer.rvh_deleted());
1419  rvh1 = NULL;
1420}
1421
1422// Tests that the RenderViewHost is properly swapped out when the SwapOutACK is
1423// received before the new page commits.
1424TEST_F(RenderFrameHostManagerTest,
1425       SwapOutACKBeforeNewPageCommitsLeadsToSwapOut) {
1426  const GURL kUrl1("http://www.google.com/");
1427  const GURL kUrl2("http://www.chromium.org/");
1428
1429  // Navigate to the first page.
1430  contents()->NavigateAndCommit(kUrl1);
1431  TestRenderViewHost* rvh1 = test_rvh();
1432  RenderViewHostDestructionObserver destruction_observer(rvh1);
1433  EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
1434
1435  // Increment the number of active views in SiteInstanceImpl so that rvh2 is
1436  // not deleted on swap out.
1437  static_cast<SiteInstanceImpl*>(
1438      rvh1->GetSiteInstance())->increment_active_view_count();
1439
1440  // Navigate to new site, simulating onbeforeunload approval.
1441  controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
1442  base::TimeTicks now = base::TimeTicks::Now();
1443  main_test_rfh()->OnMessageReceived(
1444      FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
1445  EXPECT_TRUE(contents()->cross_navigation_pending());
1446  TestRenderViewHost* rvh2 =
1447      static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
1448
1449  // Simulate rvh2's response, which leads to an unload request being sent to
1450  // rvh1.
1451  std::vector<GURL> url_chain;
1452  url_chain.push_back(GURL());
1453  contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
1454      contents()->GetRenderManagerForTesting()->pending_frame_host(),
1455      GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
1456      url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
1457  EXPECT_TRUE(contents()->cross_navigation_pending());
1458  EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK,
1459            rvh1->rvh_state());
1460
1461  // Simulate the swap out ack.
1462  rvh1->OnSwappedOut(false);
1463  EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT, rvh1->rvh_state());
1464
1465  // The new page commits.
1466  contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED);
1467  EXPECT_FALSE(contents()->cross_navigation_pending());
1468  EXPECT_EQ(rvh2, rvh());
1469  EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
1470  EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state());
1471
1472  // rvh1 should be swapped out.
1473  EXPECT_FALSE(destruction_observer.rvh_deleted());
1474  EXPECT_TRUE(rvh1->IsSwappedOut());
1475}
1476
1477// Tests that the RenderViewHost is properly deleted when the new
1478// page commits before the swap out ack is received.
1479TEST_F(RenderFrameHostManagerTest,
1480       NewPageCommitsBeforeSwapOutACKLeadsToDeletion) {
1481  const GURL kUrl1("http://www.google.com/");
1482  const GURL kUrl2("http://www.chromium.org/");
1483
1484  // Navigate to the first page.
1485  contents()->NavigateAndCommit(kUrl1);
1486  TestRenderViewHost* rvh1 = test_rvh();
1487  RenderViewHostDestructionObserver destruction_observer(rvh1);
1488  EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
1489
1490  // Navigate to new site, simulating onbeforeunload approval.
1491  controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
1492  base::TimeTicks now = base::TimeTicks::Now();
1493  main_test_rfh()->OnMessageReceived(
1494      FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
1495  EXPECT_TRUE(contents()->cross_navigation_pending());
1496  TestRenderViewHost* rvh2 =
1497      static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
1498
1499  // Simulate rvh2's response, which leads to an unload request being sent to
1500  // rvh1.
1501  std::vector<GURL> url_chain;
1502  url_chain.push_back(GURL());
1503  contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
1504      contents()->GetRenderManagerForTesting()->pending_frame_host(),
1505      GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
1506      url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
1507  EXPECT_TRUE(contents()->cross_navigation_pending());
1508  EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK,
1509            rvh1->rvh_state());
1510
1511  // The new page commits.
1512  contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED);
1513  EXPECT_FALSE(contents()->cross_navigation_pending());
1514  EXPECT_EQ(rvh2, rvh());
1515  EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
1516  EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state());
1517  EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SHUTDOWN, rvh1->rvh_state());
1518
1519  // Simulate the swap out ack.
1520  rvh1->OnSwappedOut(false);
1521
1522  // rvh1 should have been deleted.
1523  EXPECT_TRUE(destruction_observer.rvh_deleted());
1524  rvh1 = NULL;
1525}
1526
1527// Tests that the RenderViewHost is properly swapped out when the new page
1528// commits before the swap out ack is received.
1529TEST_F(RenderFrameHostManagerTest,
1530       NewPageCommitsBeforeSwapOutACKLeadsToSwapOut) {
1531  const GURL kUrl1("http://www.google.com/");
1532  const GURL kUrl2("http://www.chromium.org/");
1533
1534  // Navigate to the first page.
1535  contents()->NavigateAndCommit(kUrl1);
1536  TestRenderViewHost* rvh1 = test_rvh();
1537  RenderViewHostDestructionObserver destruction_observer(rvh1);
1538  EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
1539
1540  // Increment the number of active views in SiteInstanceImpl so that rvh1 is
1541  // not deleted on swap out.
1542  static_cast<SiteInstanceImpl*>(
1543      rvh1->GetSiteInstance())->increment_active_view_count();
1544
1545  // Navigate to new site, simulating onbeforeunload approval.
1546  controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
1547  base::TimeTicks now = base::TimeTicks::Now();
1548  main_test_rfh()->OnMessageReceived(
1549      FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
1550  EXPECT_TRUE(contents()->cross_navigation_pending());
1551  TestRenderViewHost* rvh2 =
1552      static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
1553
1554  // Simulate rvh2's response, which leads to an unload request being sent to
1555  // rvh1.
1556  std::vector<GURL> url_chain;
1557  url_chain.push_back(GURL());
1558  contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
1559      contents()->GetRenderManagerForTesting()->pending_frame_host(),
1560      GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
1561      url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
1562  EXPECT_TRUE(contents()->cross_navigation_pending());
1563  EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK,
1564            rvh1->rvh_state());
1565
1566  // The new page commits.
1567  contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED);
1568  EXPECT_FALSE(contents()->cross_navigation_pending());
1569  EXPECT_EQ(rvh2, rvh());
1570  EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
1571  EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state());
1572  EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT, rvh1->rvh_state());
1573
1574  // Simulate the swap out ack.
1575  rvh1->OnSwappedOut(false);
1576
1577  // rvh1 should be swapped out.
1578  EXPECT_FALSE(destruction_observer.rvh_deleted());
1579  EXPECT_TRUE(rvh1->IsSwappedOut());
1580}
1581
1582}  // namespace content
1583