render_frame_host_manager_unittest.cc revision a02191e04bc25c4935f804f2c080ae28663d096d
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/files/file_path.h"
6#include "base/strings/utf_string_conversions.h"
7#include "content/browser/frame_host/cross_site_transferring_request.h"
8#include "content/browser/frame_host/navigation_controller_impl.h"
9#include "content/browser/frame_host/navigation_entry_impl.h"
10#include "content/browser/frame_host/navigator.h"
11#include "content/browser/frame_host/render_frame_host_manager.h"
12#include "content/browser/site_instance_impl.h"
13#include "content/browser/webui/web_ui_controller_factory_registry.h"
14#include "content/common/frame_messages.h"
15#include "content/common/view_messages.h"
16#include "content/public/browser/notification_details.h"
17#include "content/public/browser/notification_service.h"
18#include "content/public/browser/notification_source.h"
19#include "content/public/browser/notification_types.h"
20#include "content/public/browser/render_process_host.h"
21#include "content/public/browser/render_widget_host_iterator.h"
22#include "content/public/browser/web_contents_delegate.h"
23#include "content/public/browser/web_contents_observer.h"
24#include "content/public/browser/web_ui_controller.h"
25#include "content/public/common/bindings_policy.h"
26#include "content/public/common/javascript_message_type.h"
27#include "content/public/common/page_transition_types.h"
28#include "content/public/common/url_constants.h"
29#include "content/public/common/url_utils.h"
30#include "content/public/test/mock_render_process_host.h"
31#include "content/public/test/test_notification_tracker.h"
32#include "content/test/test_content_browser_client.h"
33#include "content/test/test_content_client.h"
34#include "content/test/test_render_view_host.h"
35#include "content/test/test_web_contents.h"
36#include "testing/gtest/include/gtest/gtest.h"
37
38namespace content {
39namespace {
40
41class RenderFrameHostManagerTestWebUIControllerFactory
42    : public WebUIControllerFactory {
43 public:
44  RenderFrameHostManagerTestWebUIControllerFactory()
45    : should_create_webui_(false) {
46  }
47  virtual ~RenderFrameHostManagerTestWebUIControllerFactory() {}
48
49  void set_should_create_webui(bool should_create_webui) {
50    should_create_webui_ = should_create_webui;
51  }
52
53  // WebUIFactory implementation.
54  virtual WebUIController* CreateWebUIControllerForURL(
55      WebUI* web_ui, const GURL& url) const OVERRIDE {
56    if (!(should_create_webui_ && HasWebUIScheme(url)))
57      return NULL;
58    return new WebUIController(web_ui);
59  }
60
61   virtual WebUI::TypeID GetWebUIType(BrowserContext* browser_context,
62      const GURL& url) const OVERRIDE {
63    return WebUI::kNoWebUI;
64  }
65
66  virtual bool UseWebUIForURL(BrowserContext* browser_context,
67                              const GURL& url) const OVERRIDE {
68    return HasWebUIScheme(url);
69  }
70
71  virtual bool UseWebUIBindingsForURL(BrowserContext* browser_context,
72                                      const GURL& url) const OVERRIDE {
73    return HasWebUIScheme(url);
74  }
75
76 private:
77  bool should_create_webui_;
78
79  DISALLOW_COPY_AND_ASSIGN(RenderFrameHostManagerTestWebUIControllerFactory);
80};
81
82class BeforeUnloadFiredWebContentsDelegate : public WebContentsDelegate {
83 public:
84  BeforeUnloadFiredWebContentsDelegate() {}
85  virtual ~BeforeUnloadFiredWebContentsDelegate() {}
86
87  virtual void BeforeUnloadFired(WebContents* web_contents,
88                                 bool proceed,
89                                 bool* proceed_to_fire_unload) OVERRIDE {
90    *proceed_to_fire_unload = proceed;
91  }
92
93 private:
94  DISALLOW_COPY_AND_ASSIGN(BeforeUnloadFiredWebContentsDelegate);
95};
96
97// This observer keeps track of the last deleted RenderViewHost to avoid
98// accessing it and causing use-after-free condition.
99class RenderViewHostDeletedObserver : public WebContentsObserver {
100 public:
101  RenderViewHostDeletedObserver(RenderViewHost* rvh)
102      : WebContentsObserver(WebContents::FromRenderViewHost(rvh)),
103        process_id_(rvh->GetProcess()->GetID()),
104        routing_id_(rvh->GetRoutingID()),
105        deleted_(false) {
106  }
107
108  virtual void RenderViewDeleted(RenderViewHost* render_view_host) OVERRIDE {
109    if (render_view_host->GetProcess()->GetID() == process_id_ &&
110        render_view_host->GetRoutingID() == routing_id_) {
111      deleted_ = true;
112    }
113  }
114
115  bool deleted() {
116    return deleted_;
117  }
118
119 private:
120  int process_id_;
121  int routing_id_;
122  bool deleted_;
123
124  DISALLOW_COPY_AND_ASSIGN(RenderViewHostDeletedObserver);
125};
126
127
128// This observer keeps track of the last deleted RenderViewHost to avoid
129// accessing it and causing use-after-free condition.
130class RenderFrameHostDeletedObserver : public WebContentsObserver {
131 public:
132  RenderFrameHostDeletedObserver(RenderFrameHost* rfh)
133      : WebContentsObserver(WebContents::FromRenderFrameHost(rfh)),
134        process_id_(rfh->GetProcess()->GetID()),
135        routing_id_(rfh->GetRoutingID()),
136        deleted_(false) {
137  }
138
139  virtual void RenderFrameDeleted(RenderFrameHost* render_frame_host) OVERRIDE {
140    if (render_frame_host->GetProcess()->GetID() == process_id_ &&
141        render_frame_host->GetRoutingID() == routing_id_) {
142      deleted_ = true;
143    }
144  }
145
146  bool deleted() {
147    return deleted_;
148  }
149
150 private:
151  int process_id_;
152  int routing_id_;
153  bool deleted_;
154
155  DISALLOW_COPY_AND_ASSIGN(RenderFrameHostDeletedObserver);
156};
157
158
159// This observer is used to check whether IPC messages are being filtered for
160// swapped out RenderFrameHost objects. It observes the plugin crash and favicon
161// update events, which the FilterMessagesWhileSwappedOut test simulates being
162// sent. The test is successful if the event is not observed.
163// See http://crbug.com/351815
164class PluginFaviconMessageObserver : public WebContentsObserver {
165 public:
166  PluginFaviconMessageObserver(WebContents* web_contents)
167      : WebContentsObserver(web_contents),
168        plugin_crashed_(false),
169        favicon_received_(false) { }
170
171  virtual void PluginCrashed(const base::FilePath& plugin_path,
172                             base::ProcessId plugin_pid) OVERRIDE {
173    plugin_crashed_ = true;
174  }
175
176  virtual void DidUpdateFaviconURL(
177      int32 page_id,
178      const std::vector<FaviconURL>& candidates) OVERRIDE {
179    favicon_received_ = true;
180  }
181
182  bool plugin_crashed() {
183    return plugin_crashed_;
184  }
185
186  bool favicon_received() {
187    return favicon_received_;
188  }
189
190 private:
191  bool plugin_crashed_;
192  bool favicon_received_;
193
194  DISALLOW_COPY_AND_ASSIGN(PluginFaviconMessageObserver);
195};
196
197
198}  // namespace
199
200class RenderFrameHostManagerTest
201    : public RenderViewHostImplTestHarness {
202 public:
203  virtual void SetUp() OVERRIDE {
204    RenderViewHostImplTestHarness::SetUp();
205    WebUIControllerFactory::RegisterFactory(&factory_);
206  }
207
208  virtual void TearDown() OVERRIDE {
209    RenderViewHostImplTestHarness::TearDown();
210    WebUIControllerFactory::UnregisterFactoryForTesting(&factory_);
211  }
212
213  void set_should_create_webui(bool should_create_webui) {
214    factory_.set_should_create_webui(should_create_webui);
215  }
216
217  void StartCrossSiteTransition(TestWebContents* contents) {
218    std::vector<GURL> url_chain;
219    contents->GetRenderManagerForTesting()->OnCrossSiteResponse(
220        contents->GetRenderManagerForTesting()->pending_frame_host(),
221        GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
222        url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
223    EXPECT_TRUE(contents->cross_navigation_pending());
224    RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
225        contents->GetRenderViewHost());
226    EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK,
227              rvh->rvh_state());
228  }
229
230  void NavigateActiveAndCommit(const GURL& url) {
231    // Note: we navigate the active RenderViewHost because previous navigations
232    // won't have committed yet, so NavigateAndCommit does the wrong thing
233    // for us.
234    controller().LoadURL(url, Referrer(), PAGE_TRANSITION_LINK, std::string());
235    TestRenderViewHost* old_rvh = test_rvh();
236
237    // Simulate the BeforeUnload_ACK that is received from the current renderer
238    // for a cross-site navigation.
239    if (old_rvh != active_rvh()) {
240      old_rvh->SendBeforeUnloadACK(true);
241      EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, old_rvh->rvh_state());
242    }
243
244    // Commit the navigation with a new page ID.
245    int32 max_page_id = contents()->GetMaxPageIDForSiteInstance(
246        active_rvh()->GetSiteInstance());
247
248    // Simulate the response coming from the pending renderer.
249    if (old_rvh != active_rvh())
250      StartCrossSiteTransition(contents());
251
252    // Simulate the SwapOut_ACK that fires if you commit a cross-site
253    // navigation.
254    if (old_rvh != active_rvh()) {
255      old_rvh->OnSwappedOut(false);
256      EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT,
257                old_rvh->rvh_state());
258    }
259
260    // Use an observer to avoid accessing a deleted renderer later on when the
261    // state is being checked.
262    RenderViewHostDeletedObserver rvh_observer(old_rvh);
263    active_test_rvh()->SendNavigate(max_page_id + 1, url);
264
265    if (old_rvh != active_rvh() && !rvh_observer.deleted())
266      EXPECT_TRUE(old_rvh->IsSwappedOut());
267  }
268
269  bool ShouldSwapProcesses(RenderFrameHostManager* manager,
270                           const NavigationEntryImpl* current_entry,
271                           const NavigationEntryImpl* new_entry) const {
272    return manager->ShouldSwapBrowsingInstancesForNavigation(current_entry,
273                                                             new_entry);
274  }
275
276  // Creates a test RenderViewHost that's swapped out.
277  TestRenderViewHost* CreateSwappedOutRenderViewHost() {
278    const GURL kChromeURL("chrome://foo");
279    const GURL kDestUrl("http://www.google.com/");
280
281    // Navigate our first tab to a chrome url and then to the destination.
282    NavigateActiveAndCommit(kChromeURL);
283    TestRenderViewHost* ntp_rvh = static_cast<TestRenderViewHost*>(
284        contents()->GetRenderManagerForTesting()->current_host());
285
286    // Navigate to a cross-site URL.
287    contents()->GetController().LoadURL(
288        kDestUrl, Referrer(), PAGE_TRANSITION_LINK, std::string());
289    EXPECT_TRUE(contents()->cross_navigation_pending());
290
291    // Manually increase the number of active views in the
292    // SiteInstance that ntp_rvh belongs to, to prevent it from being
293    // destroyed when it gets swapped out.
294    static_cast<SiteInstanceImpl*>(ntp_rvh->GetSiteInstance())->
295        increment_active_view_count();
296
297    TestRenderViewHost* dest_rvh = static_cast<TestRenderViewHost*>(
298        contents()->GetRenderManagerForTesting()->pending_render_view_host());
299    CHECK(dest_rvh);
300    EXPECT_NE(ntp_rvh, dest_rvh);
301
302    // BeforeUnload finishes.
303    ntp_rvh->SendBeforeUnloadACK(true);
304
305    dest_rvh->SendNavigate(101, kDestUrl);
306    ntp_rvh->OnSwappedOut(false);
307
308    EXPECT_TRUE(ntp_rvh->IsSwappedOut());
309    return ntp_rvh;
310  }
311
312 private:
313  RenderFrameHostManagerTestWebUIControllerFactory factory_;
314};
315
316// Tests that when you navigate from a chrome:// url to another page, and
317// then do that same thing in another tab, that the two resulting pages have
318// different SiteInstances, BrowsingInstances, and RenderProcessHosts. This is
319// a regression test for bug 9364.
320TEST_F(RenderFrameHostManagerTest, NewTabPageProcesses) {
321  set_should_create_webui(true);
322  const GURL kChromeUrl("chrome://foo");
323  const GURL kDestUrl("http://www.google.com/");
324
325  // Navigate our first tab to the chrome url and then to the destination,
326  // ensuring we grant bindings to the chrome URL.
327  NavigateActiveAndCommit(kChromeUrl);
328  EXPECT_TRUE(active_rvh()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
329  NavigateActiveAndCommit(kDestUrl);
330
331  // Make a second tab.
332  scoped_ptr<TestWebContents> contents2(
333      TestWebContents::Create(browser_context(), NULL));
334
335  // Load the two URLs in the second tab. Note that the first navigation creates
336  // a RVH that's not pending (since there is no cross-site transition), so
337  // we use the committed one.
338  contents2->GetController().LoadURL(
339      kChromeUrl, Referrer(), PAGE_TRANSITION_LINK, std::string());
340  TestRenderViewHost* ntp_rvh2 = static_cast<TestRenderViewHost*>(
341      contents2->GetRenderManagerForTesting()->current_host());
342  EXPECT_FALSE(contents2->cross_navigation_pending());
343  ntp_rvh2->SendNavigate(100, kChromeUrl);
344
345  // The second one is the opposite, creating a cross-site transition and
346  // requiring a beforeunload ack.
347  contents2->GetController().LoadURL(
348      kDestUrl, Referrer(), PAGE_TRANSITION_LINK, std::string());
349  EXPECT_TRUE(contents2->cross_navigation_pending());
350  TestRenderViewHost* dest_rvh2 = static_cast<TestRenderViewHost*>(
351      contents2->GetRenderManagerForTesting()->pending_render_view_host());
352  ASSERT_TRUE(dest_rvh2);
353
354  ntp_rvh2->SendBeforeUnloadACK(true);
355  StartCrossSiteTransition(contents2.get());
356  dest_rvh2->SendNavigate(101, kDestUrl);
357
358  // The two RVH's should be different in every way.
359  EXPECT_NE(active_rvh()->GetProcess(), dest_rvh2->GetProcess());
360  EXPECT_NE(active_rvh()->GetSiteInstance(), dest_rvh2->GetSiteInstance());
361  EXPECT_FALSE(active_rvh()->GetSiteInstance()->IsRelatedSiteInstance(
362                   dest_rvh2->GetSiteInstance()));
363
364  // Navigate both to the new tab page, and verify that they share a
365  // RenderProcessHost (not a SiteInstance).
366  NavigateActiveAndCommit(kChromeUrl);
367
368  contents2->GetController().LoadURL(
369      kChromeUrl, Referrer(), PAGE_TRANSITION_LINK, std::string());
370  dest_rvh2->SendBeforeUnloadACK(true);
371  StartCrossSiteTransition(contents2.get());
372  static_cast<TestRenderViewHost*>(contents2->GetRenderManagerForTesting()->
373      pending_render_view_host())->SendNavigate(102, kChromeUrl);
374
375  EXPECT_NE(active_rvh()->GetSiteInstance(),
376            contents2->GetRenderViewHost()->GetSiteInstance());
377  EXPECT_EQ(active_rvh()->GetSiteInstance()->GetProcess(),
378            contents2->GetRenderViewHost()->GetSiteInstance()->GetProcess());
379}
380
381// Ensure that the browser ignores most IPC messages that arrive from a
382// RenderViewHost that has been swapped out.  We do not want to take
383// action on requests from a non-active renderer.  The main exception is
384// for synchronous messages, which cannot be ignored without leaving the
385// renderer in a stuck state.  See http://crbug.com/93427.
386TEST_F(RenderFrameHostManagerTest, FilterMessagesWhileSwappedOut) {
387  const GURL kChromeURL("chrome://foo");
388  const GURL kDestUrl("http://www.google.com/");
389  std::vector<FaviconURL> icons;
390
391  // Navigate our first tab to a chrome url and then to the destination.
392  NavigateActiveAndCommit(kChromeURL);
393  TestRenderViewHost* ntp_rvh = static_cast<TestRenderViewHost*>(
394      contents()->GetRenderManagerForTesting()->current_host());
395
396  // Send an update favicon message and make sure it works.
397  const base::string16 ntp_title = base::ASCIIToUTF16("NTP Title");
398  {
399    PluginFaviconMessageObserver observer(contents());
400    EXPECT_TRUE(ntp_rvh->OnMessageReceived(
401                    ViewHostMsg_UpdateFaviconURL(
402                        rvh()->GetRoutingID(), 0, icons)));
403    EXPECT_TRUE(observer.favicon_received());
404  }
405  // Create one more view in the same SiteInstance where ntp_rvh
406  // exists so that it doesn't get deleted on navigation to another
407  // site.
408  static_cast<SiteInstanceImpl*>(ntp_rvh->GetSiteInstance())->
409      increment_active_view_count();
410
411
412  // Navigate to a cross-site URL.
413  NavigateActiveAndCommit(kDestUrl);
414  TestRenderViewHost* dest_rvh = static_cast<TestRenderViewHost*>(
415      contents()->GetRenderViewHost());
416  ASSERT_TRUE(dest_rvh);
417  EXPECT_NE(ntp_rvh, dest_rvh);
418
419  // The new RVH should be able to update its favicon.
420  const base::string16 dest_title = base::ASCIIToUTF16("Google");
421  {
422    PluginFaviconMessageObserver observer(contents());
423    EXPECT_TRUE(
424        dest_rvh->OnMessageReceived(
425            ViewHostMsg_UpdateFaviconURL(rvh()->GetRoutingID(), 101, icons)));
426    EXPECT_TRUE(observer.favicon_received());
427  }
428
429  // The old renderer, being slow, now updates the favicon. It should be
430  // filtered out and not take effect.
431  EXPECT_TRUE(ntp_rvh->IsSwappedOut());
432  {
433    PluginFaviconMessageObserver observer(contents());
434    EXPECT_TRUE(
435        ntp_rvh->OnMessageReceived(
436            ViewHostMsg_UpdateFaviconURL(rvh()->GetRoutingID(), 101, icons)));
437    EXPECT_FALSE(observer.favicon_received());
438  }
439
440  // The same logic should apply to RenderFrameHosts as well and routing through
441  // swapped out RFH shouldn't be allowed. Use a PluginCrashObserver to check
442  // if the IPC message is allowed through or not.
443  {
444    PluginFaviconMessageObserver observer(contents());
445    // TODO(nasko): Check that the RFH is in swapped out when the state moves
446    // from RVH to RFH.
447    EXPECT_TRUE(ntp_rvh->main_render_frame_host()->OnMessageReceived(
448                    FrameHostMsg_PluginCrashed(
449                        main_rfh()->GetRoutingID(), base::FilePath(), 0)));
450    EXPECT_FALSE(observer.plugin_crashed());
451  }
452
453  // We cannot filter out synchronous IPC messages, because the renderer would
454  // be left waiting for a reply.  We pick RunBeforeUnloadConfirm as an example
455  // that can run easily within a unit test, and that needs to receive a reply
456  // without showing an actual dialog.
457  MockRenderProcessHost* ntp_process_host =
458      static_cast<MockRenderProcessHost*>(ntp_rvh->GetProcess());
459  ntp_process_host->sink().ClearMessages();
460  RenderFrameHost* ntp_rfh = ntp_rvh->GetMainFrame();
461  const base::string16 msg = base::ASCIIToUTF16("Message");
462  bool result = false;
463  base::string16 unused;
464  FrameHostMsg_RunBeforeUnloadConfirm before_unload_msg(
465      ntp_rfh->GetRoutingID(), kChromeURL, msg, false, &result, &unused);
466  // Enable pumping for check in BrowserMessageFilter::CheckCanDispatchOnUI.
467  before_unload_msg.EnableMessagePumping();
468  EXPECT_TRUE(ntp_rfh->OnMessageReceived(before_unload_msg));
469  EXPECT_TRUE(ntp_process_host->sink().GetUniqueMessageMatching(IPC_REPLY_ID));
470
471  // Also test RunJavaScriptMessage.
472  ntp_process_host->sink().ClearMessages();
473  FrameHostMsg_RunJavaScriptMessage js_msg(
474      ntp_rfh->GetRoutingID(), msg, msg, kChromeURL,
475      JAVASCRIPT_MESSAGE_TYPE_CONFIRM, &result, &unused);
476  js_msg.EnableMessagePumping();
477  EXPECT_TRUE(ntp_rfh->OnMessageReceived(js_msg));
478  EXPECT_TRUE(ntp_process_host->sink().GetUniqueMessageMatching(IPC_REPLY_ID));
479}
480
481TEST_F(RenderFrameHostManagerTest, WhiteListSwapCompositorFrame) {
482  TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost();
483  TestRenderWidgetHostView* swapped_out_rwhv =
484      static_cast<TestRenderWidgetHostView*>(swapped_out_rvh->GetView());
485  EXPECT_FALSE(swapped_out_rwhv->did_swap_compositor_frame());
486
487  MockRenderProcessHost* process_host =
488      static_cast<MockRenderProcessHost*>(swapped_out_rvh->GetProcess());
489  process_host->sink().ClearMessages();
490
491  cc::CompositorFrame frame;
492  ViewHostMsg_SwapCompositorFrame msg(rvh()->GetRoutingID(), 0, frame);
493
494  EXPECT_TRUE(swapped_out_rvh->OnMessageReceived(msg));
495  EXPECT_TRUE(swapped_out_rwhv->did_swap_compositor_frame());
496}
497
498TEST_F(RenderFrameHostManagerTest, WhiteListDidActivateAcceleratedCompositing) {
499  TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost();
500
501  MockRenderProcessHost* process_host =
502      static_cast<MockRenderProcessHost*>(swapped_out_rvh->GetProcess());
503  process_host->sink().ClearMessages();
504  ViewHostMsg_DidActivateAcceleratedCompositing msg(
505      rvh()->GetRoutingID(), true);
506  EXPECT_TRUE(swapped_out_rvh->OnMessageReceived(msg));
507  EXPECT_TRUE(swapped_out_rvh->is_accelerated_compositing_active());
508}
509
510// Test if RenderViewHost::GetRenderWidgetHosts() only returns active
511// widgets.
512TEST_F(RenderFrameHostManagerTest, GetRenderWidgetHostsReturnsActiveViews) {
513  TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost();
514  EXPECT_TRUE(swapped_out_rvh->IsSwappedOut());
515
516  scoped_ptr<RenderWidgetHostIterator> widgets(
517      RenderWidgetHost::GetRenderWidgetHosts());
518  // We know that there is the only one active widget. Another view is
519  // now swapped out, so the swapped out view is not included in the
520  // list.
521  RenderWidgetHost* widget = widgets->GetNextHost();
522  EXPECT_FALSE(widgets->GetNextHost());
523  RenderViewHost* rvh = RenderViewHost::From(widget);
524  EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT,
525            static_cast<RenderViewHostImpl*>(rvh)->rvh_state());
526}
527
528// Test if RenderViewHost::GetRenderWidgetHosts() returns a subset of
529// RenderViewHostImpl::GetAllRenderWidgetHosts().
530// RenderViewHost::GetRenderWidgetHosts() returns only active widgets, but
531// RenderViewHostImpl::GetAllRenderWidgetHosts() returns everything
532// including swapped out ones.
533TEST_F(RenderFrameHostManagerTest,
534       GetRenderWidgetHostsWithinGetAllRenderWidgetHosts) {
535  TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost();
536  EXPECT_TRUE(swapped_out_rvh->IsSwappedOut());
537
538  scoped_ptr<RenderWidgetHostIterator> widgets(
539      RenderWidgetHost::GetRenderWidgetHosts());
540
541  while (RenderWidgetHost* w = widgets->GetNextHost()) {
542    bool found = false;
543    scoped_ptr<RenderWidgetHostIterator> all_widgets(
544        RenderWidgetHostImpl::GetAllRenderWidgetHosts());
545    while (RenderWidgetHost* widget = all_widgets->GetNextHost()) {
546      if (w == widget) {
547        found = true;
548        break;
549      }
550    }
551    EXPECT_TRUE(found);
552  }
553}
554
555// Test if SiteInstanceImpl::active_view_count() is correctly updated
556// as views in a SiteInstance get swapped out and in.
557TEST_F(RenderFrameHostManagerTest, ActiveViewCountWhileSwappingInandOut) {
558  const GURL kUrl1("http://www.google.com/");
559  const GURL kUrl2("http://www.chromium.org/");
560
561  // Navigate to an initial URL.
562  contents()->NavigateAndCommit(kUrl1);
563  TestRenderViewHost* rvh1 = test_rvh();
564
565  SiteInstanceImpl* instance1 =
566      static_cast<SiteInstanceImpl*>(rvh1->GetSiteInstance());
567  EXPECT_EQ(instance1->active_view_count(), 1U);
568
569  // Create 2 new tabs and simulate them being the opener chain for the main
570  // tab.  They should be in the same SiteInstance.
571  scoped_ptr<TestWebContents> opener1(
572      TestWebContents::Create(browser_context(), instance1));
573  contents()->SetOpener(opener1.get());
574
575  scoped_ptr<TestWebContents> opener2(
576      TestWebContents::Create(browser_context(), instance1));
577  opener1->SetOpener(opener2.get());
578
579  EXPECT_EQ(instance1->active_view_count(), 3U);
580
581  // Navigate to a cross-site URL (different SiteInstance but same
582  // BrowsingInstance).
583  contents()->NavigateAndCommit(kUrl2);
584  TestRenderViewHost* rvh2 = test_rvh();
585  SiteInstanceImpl* instance2 =
586      static_cast<SiteInstanceImpl*>(rvh2->GetSiteInstance());
587
588  // rvh2 is on chromium.org which is different from google.com on
589  // which other tabs are.
590  EXPECT_EQ(instance2->active_view_count(), 1U);
591
592  // There are two active views on google.com now.
593  EXPECT_EQ(instance1->active_view_count(), 2U);
594
595  // Navigate to the original origin (google.com).
596  contents()->NavigateAndCommit(kUrl1);
597
598  EXPECT_EQ(instance1->active_view_count(), 3U);
599}
600
601// This deletes a WebContents when the given RVH is deleted. This is
602// only for testing whether deleting an RVH does not cause any UaF in
603// other parts of the system. For now, this class is only used for the
604// next test cases to detect the bug mentioned at
605// http://crbug.com/259859.
606class RenderViewHostDestroyer : public WebContentsObserver {
607 public:
608  RenderViewHostDestroyer(RenderViewHost* render_view_host,
609                          WebContents* web_contents)
610      : WebContentsObserver(WebContents::FromRenderViewHost(render_view_host)),
611        render_view_host_(render_view_host),
612        web_contents_(web_contents) {}
613
614  virtual void RenderViewDeleted(
615      RenderViewHost* render_view_host) OVERRIDE {
616    if (render_view_host == render_view_host_)
617      delete web_contents_;
618  }
619
620 private:
621  RenderViewHost* render_view_host_;
622  WebContents* web_contents_;
623
624  DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestroyer);
625};
626
627// Test if ShutdownRenderViewHostsInSiteInstance() does not touch any
628// RenderWidget that has been freed while deleting a RenderViewHost in
629// a previous iteration. This is a regression test for
630// http://crbug.com/259859.
631TEST_F(RenderFrameHostManagerTest,
632       DetectUseAfterFreeInShutdownRenderViewHostsInSiteInstance) {
633  const GURL kChromeURL("chrome://newtab");
634  const GURL kUrl1("http://www.google.com");
635  const GURL kUrl2("http://www.chromium.org");
636
637  // Navigate our first tab to a chrome url and then to the destination.
638  NavigateActiveAndCommit(kChromeURL);
639  TestRenderViewHost* ntp_rvh = static_cast<TestRenderViewHost*>(
640      contents()->GetRenderManagerForTesting()->current_host());
641
642  // Create one more tab and navigate to kUrl1.  web_contents is not
643  // wrapped as scoped_ptr since it intentionally deleted by destroyer
644  // below as part of this test.
645  TestWebContents* web_contents =
646      TestWebContents::Create(browser_context(), ntp_rvh->GetSiteInstance());
647  web_contents->NavigateAndCommit(kUrl1);
648  RenderViewHostDestroyer destroyer(ntp_rvh, web_contents);
649
650  // This causes the first tab to navigate to kUrl2, which destroys
651  // the ntp_rvh in ShutdownRenderViewHostsInSiteInstance(). When
652  // ntp_rvh is destroyed, it also destroys the RVHs in web_contents
653  // too. This can test whether
654  // SiteInstanceImpl::ShutdownRenderViewHostsInSiteInstance() can
655  // touch any object freed in this way or not while iterating through
656  // all widgets.
657  contents()->NavigateAndCommit(kUrl2);
658}
659
660// When there is an error with the specified page, renderer exits view-source
661// mode. See WebFrameImpl::DidFail(). We check by this test that
662// EnableViewSourceMode message is sent on every navigation regardless
663// RenderView is being newly created or reused.
664TEST_F(RenderFrameHostManagerTest, AlwaysSendEnableViewSourceMode) {
665  const GURL kChromeUrl("chrome://foo");
666  const GURL kUrl("view-source:http://foo");
667
668  // We have to navigate to some page at first since without this, the first
669  // navigation will reuse the SiteInstance created by Init(), and the second
670  // one will create a new SiteInstance. Because current_instance and
671  // new_instance will be different, a new RenderViewHost will be created for
672  // the second navigation. We have to avoid this in order to exercise the
673  // target code patch.
674  NavigateActiveAndCommit(kChromeUrl);
675
676  // Navigate.
677  controller().LoadURL(
678      kUrl, Referrer(), PAGE_TRANSITION_TYPED, std::string());
679  // Simulate response from RenderFrame for DispatchBeforeUnload.
680  base::TimeTicks now = base::TimeTicks::Now();
681  main_test_rfh()->OnMessageReceived(FrameHostMsg_BeforeUnload_ACK(
682      main_test_rfh()->GetRoutingID(), true, now, now));
683  ASSERT_TRUE(pending_rvh());  // New pending RenderViewHost will be created.
684  RenderViewHost* last_rvh = pending_rvh();
685  int32 new_id = contents()->GetMaxPageIDForSiteInstance(
686      active_rvh()->GetSiteInstance()) + 1;
687  pending_test_rvh()->SendNavigate(new_id, kUrl);
688  EXPECT_EQ(controller().GetLastCommittedEntryIndex(), 1);
689  ASSERT_TRUE(controller().GetLastCommittedEntry());
690  EXPECT_TRUE(kUrl == controller().GetLastCommittedEntry()->GetURL());
691  EXPECT_FALSE(controller().GetPendingEntry());
692  // Because we're using TestWebContents and TestRenderViewHost in this
693  // unittest, no one calls WebContentsImpl::RenderViewCreated(). So, we see no
694  // EnableViewSourceMode message, here.
695
696  // Clear queued messages before load.
697  process()->sink().ClearMessages();
698  // Navigate, again.
699  controller().LoadURL(
700      kUrl, Referrer(), PAGE_TRANSITION_TYPED, std::string());
701  // The same RenderViewHost should be reused.
702  EXPECT_FALSE(pending_rvh());
703  EXPECT_TRUE(last_rvh == rvh());
704  test_rvh()->SendNavigate(new_id, kUrl);  // The same page_id returned.
705  EXPECT_EQ(controller().GetLastCommittedEntryIndex(), 1);
706  EXPECT_FALSE(controller().GetPendingEntry());
707  // New message should be sent out to make sure to enter view-source mode.
708  EXPECT_TRUE(process()->sink().GetUniqueMessageMatching(
709      ViewMsg_EnableViewSourceMode::ID));
710}
711
712// Tests the Init function by checking the initial RenderViewHost.
713TEST_F(RenderFrameHostManagerTest, Init) {
714  // Using TestBrowserContext.
715  SiteInstanceImpl* instance =
716      static_cast<SiteInstanceImpl*>(SiteInstance::Create(browser_context()));
717  EXPECT_FALSE(instance->HasSite());
718
719  scoped_ptr<TestWebContents> web_contents(
720      TestWebContents::Create(browser_context(), instance));
721  FrameTree tree(web_contents->GetFrameTree()->root()->navigator(),
722                 web_contents.get(), web_contents.get(),
723                 web_contents.get(), web_contents.get());
724  RenderFrameHostManager* manager = tree.root()->render_manager();
725
726  manager->Init(browser_context(), instance, MSG_ROUTING_NONE,
727                MSG_ROUTING_NONE);
728
729  RenderViewHostImpl* rvh = manager->current_host();
730  RenderFrameHostImpl* rfh = manager->current_frame_host();
731  ASSERT_TRUE(rvh);
732  ASSERT_TRUE(rfh);
733  EXPECT_EQ(rvh, rfh->render_view_host());
734  EXPECT_EQ(instance, rvh->GetSiteInstance());
735  EXPECT_EQ(web_contents.get(), rvh->GetDelegate());
736  EXPECT_EQ(web_contents.get(), rfh->delegate());
737  EXPECT_TRUE(manager->GetRenderWidgetHostView());
738  EXPECT_FALSE(manager->pending_render_view_host());
739}
740
741// Tests the Navigate function. We navigate three sites consecutively and check
742// how the pending/committed RenderViewHost are modified.
743TEST_F(RenderFrameHostManagerTest, Navigate) {
744  TestNotificationTracker notifications;
745
746  SiteInstance* instance = SiteInstance::Create(browser_context());
747
748  scoped_ptr<TestWebContents> web_contents(
749      TestWebContents::Create(browser_context(), instance));
750  notifications.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
751                          Source<WebContents>(web_contents.get()));
752
753  // Create.
754  FrameTree tree(web_contents->GetFrameTree()->root()->navigator(),
755                 web_contents.get(), web_contents.get(),
756                 web_contents.get(), web_contents.get());
757  RenderFrameHostManager* manager = tree.root()->render_manager();
758
759  manager->Init(browser_context(), instance, MSG_ROUTING_NONE,
760                MSG_ROUTING_NONE);
761
762  RenderFrameHostImpl* host;
763
764  // 1) The first navigation. --------------------------
765  const GURL kUrl1("http://www.google.com/");
766  NavigationEntryImpl entry1(
767      NULL /* instance */, -1 /* page_id */, kUrl1, Referrer(),
768      base::string16() /* title */, PAGE_TRANSITION_TYPED,
769      false /* is_renderer_init */);
770  host = manager->Navigate(entry1);
771
772  // The RenderFrameHost created in Init will be reused.
773  EXPECT_TRUE(host == manager->current_frame_host());
774  EXPECT_FALSE(manager->pending_frame_host());
775
776  // Commit.
777  manager->DidNavigateFrame(host);
778  // Commit to SiteInstance should be delayed until RenderView commit.
779  EXPECT_TRUE(host == manager->current_frame_host());
780  ASSERT_TRUE(host);
781  EXPECT_FALSE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
782      HasSite());
783  static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->SetSite(kUrl1);
784
785  // 2) Navigate to next site. -------------------------
786  const GURL kUrl2("http://www.google.com/foo");
787  NavigationEntryImpl entry2(
788      NULL /* instance */, -1 /* page_id */, kUrl2,
789      Referrer(kUrl1, blink::WebReferrerPolicyDefault),
790      base::string16() /* title */, PAGE_TRANSITION_LINK,
791      true /* is_renderer_init */);
792  host = manager->Navigate(entry2);
793
794  // The RenderFrameHost created in Init will be reused.
795  EXPECT_TRUE(host == manager->current_frame_host());
796  EXPECT_FALSE(manager->pending_frame_host());
797
798  // Commit.
799  manager->DidNavigateFrame(host);
800  EXPECT_TRUE(host == manager->current_frame_host());
801  ASSERT_TRUE(host);
802  EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
803      HasSite());
804
805  // 3) Cross-site navigate to next site. --------------
806  const GURL kUrl3("http://webkit.org/");
807  NavigationEntryImpl entry3(
808      NULL /* instance */, -1 /* page_id */, kUrl3,
809      Referrer(kUrl2, blink::WebReferrerPolicyDefault),
810      base::string16() /* title */, PAGE_TRANSITION_LINK,
811      false /* is_renderer_init */);
812  host = manager->Navigate(entry3);
813
814  // A new RenderFrameHost should be created.
815  EXPECT_TRUE(manager->pending_frame_host());
816  ASSERT_EQ(host, manager->pending_frame_host());
817
818  notifications.Reset();
819
820  // Commit.
821  manager->DidNavigateFrame(manager->pending_frame_host());
822  EXPECT_TRUE(host == manager->current_frame_host());
823  ASSERT_TRUE(host);
824  EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
825      HasSite());
826  // Check the pending RenderFrameHost has been committed.
827  EXPECT_FALSE(manager->pending_frame_host());
828
829  // We should observe a notification.
830  EXPECT_TRUE(
831      notifications.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED));
832}
833
834// Tests the Navigate function. In this unit test we verify that the Navigate
835// function can handle a new navigation event before the previous navigation
836// has been committed. This is also a regression test for
837// http://crbug.com/104600.
838TEST_F(RenderFrameHostManagerTest, NavigateWithEarlyReNavigation) {
839  TestNotificationTracker notifications;
840
841  SiteInstance* instance = SiteInstance::Create(browser_context());
842
843  scoped_ptr<TestWebContents> web_contents(
844      TestWebContents::Create(browser_context(), instance));
845  notifications.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
846                          Source<WebContents>(web_contents.get()));
847
848  // Create.
849  FrameTree tree(web_contents->GetFrameTree()->root()->navigator(),
850                 web_contents.get(), web_contents.get(),
851                 web_contents.get(), web_contents.get());
852  RenderFrameHostManager* manager = tree.root()->render_manager();
853
854  manager->Init(browser_context(), instance, MSG_ROUTING_NONE,
855                MSG_ROUTING_NONE);
856
857  // 1) The first navigation. --------------------------
858  const GURL kUrl1("http://www.google.com/");
859  NavigationEntryImpl entry1(NULL /* instance */, -1 /* page_id */, kUrl1,
860                             Referrer(), base::string16() /* title */,
861                             PAGE_TRANSITION_TYPED,
862                             false /* is_renderer_init */);
863  RenderFrameHostImpl* host = manager->Navigate(entry1);
864
865  // The RenderFrameHost created in Init will be reused.
866  EXPECT_TRUE(host == manager->current_frame_host());
867  EXPECT_FALSE(manager->pending_frame_host());
868
869  // We should observe a notification.
870  EXPECT_TRUE(
871      notifications.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED));
872  notifications.Reset();
873
874  // Commit.
875  manager->DidNavigateFrame(host);
876
877  // Commit to SiteInstance should be delayed until RenderView commit.
878  EXPECT_TRUE(host == manager->current_frame_host());
879  ASSERT_TRUE(host);
880  EXPECT_FALSE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
881      HasSite());
882  static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->SetSite(kUrl1);
883
884  // 2) Cross-site navigate to next site. -------------------------
885  const GURL kUrl2("http://www.example.com");
886  NavigationEntryImpl entry2(
887      NULL /* instance */, -1 /* page_id */, kUrl2, Referrer(),
888      base::string16() /* title */, PAGE_TRANSITION_TYPED,
889      false /* is_renderer_init */);
890  RenderFrameHostImpl* host2 = manager->Navigate(entry2);
891  int host2_process_id = host2->GetProcess()->GetID();
892
893  // A new RenderFrameHost should be created.
894  EXPECT_TRUE(manager->pending_frame_host());
895  ASSERT_EQ(host2, manager->pending_frame_host());
896  EXPECT_NE(host2, host);
897
898  // Check that the navigation is still suspended because the old RVH
899  // is not swapped out, yet.
900  EXPECT_TRUE(host2->render_view_host()->are_navigations_suspended());
901  MockRenderProcessHost* test_process_host2 =
902      static_cast<MockRenderProcessHost*>(host2->GetProcess());
903  test_process_host2->sink().ClearMessages();
904  host2->render_view_host()->NavigateToURL(kUrl2);
905  EXPECT_FALSE(test_process_host2->sink().GetUniqueMessageMatching(
906      FrameMsg_Navigate::ID));
907
908  // Allow closing the current Render View (precondition for swapping out
909  // the RVH): Simulate response from RenderFrame for FrameMsg_BeforeUnload sent
910  // by DispatchBeforeUnload.
911  TestRenderViewHost* test_host =
912      static_cast<TestRenderViewHost*>(host->render_view_host());
913  MockRenderProcessHost* test_process_host =
914      static_cast<MockRenderProcessHost*>(test_host->GetProcess());
915  EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching(
916      FrameMsg_BeforeUnload::ID));
917  test_host->SendBeforeUnloadACK(true);
918
919  // CrossSiteResourceHandler::StartCrossSiteTransition triggers a
920  // call of RenderFrameHostManager::SwapOutOldPage before
921  // RenderFrameHostManager::DidNavigateFrame is called.
922  // The RVH is swapped out after receiving the unload ack.
923  manager->SwapOutOldPage();
924  EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching(
925      FrameMsg_SwapOut::ID));
926  test_host->OnSwappedOut(false);
927
928  EXPECT_EQ(host, manager->current_frame_host());
929  EXPECT_FALSE(manager->current_frame_host()->is_swapped_out());
930  EXPECT_EQ(host2, manager->pending_frame_host());
931  // There should be still no navigation messages being sent.
932  EXPECT_FALSE(test_process_host2->sink().GetUniqueMessageMatching(
933      FrameMsg_Navigate::ID));
934
935  // 3) Cross-site navigate to next site before 2) has committed. --------------
936  const GURL kUrl3("http://webkit.org/");
937  NavigationEntryImpl entry3(NULL /* instance */, -1 /* page_id */, kUrl3,
938                             Referrer(), base::string16() /* title */,
939                             PAGE_TRANSITION_TYPED,
940                             false /* is_renderer_init */);
941  test_process_host->sink().ClearMessages();
942  RenderFrameHostImpl* host3 = manager->Navigate(entry3);
943
944  // A new RenderFrameHost should be created. host2 is now deleted.
945  EXPECT_TRUE(manager->pending_frame_host());
946  ASSERT_EQ(host3, manager->pending_frame_host());
947  EXPECT_NE(host3, host);
948  EXPECT_NE(host3->GetProcess()->GetID(), host2_process_id);
949
950  // Navigations in the new RVH should be suspended.
951  EXPECT_TRUE(static_cast<RenderViewHostImpl*>(
952      host3->render_view_host())->are_navigations_suspended());
953  EXPECT_EQ(host, manager->current_frame_host());
954  EXPECT_FALSE(manager->current_frame_host()->is_swapped_out());
955
956  // Simulate a response to the second beforeunload request.
957  EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching(
958      FrameMsg_BeforeUnload::ID));
959  test_host->SendBeforeUnloadACK(true);
960
961  // CrossSiteResourceHandler::StartCrossSiteTransition triggers a
962  // call of RenderFrameHostManager::SwapOutOldPage before
963  // RenderFrameHostManager::DidNavigateFrame is called. Since the previous
964  // navigation has already caused the renderer to start swapping out, there
965  // will be no more SwapOut messages being sent.
966  manager->SwapOutOldPage();
967  EXPECT_FALSE(test_process_host->sink().GetUniqueMessageMatching(
968      FrameMsg_SwapOut::ID));
969  test_host->OnSwappedOut(false);
970
971  // Commit.
972  manager->DidNavigateFrame(host3);
973  EXPECT_TRUE(host3 == manager->current_frame_host());
974  ASSERT_TRUE(host3);
975  EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host3->GetSiteInstance())->
976      HasSite());
977  // Check the pending RenderFrameHost has been committed.
978  EXPECT_FALSE(manager->pending_frame_host());
979
980  // We should observe a notification.
981  EXPECT_TRUE(
982      notifications.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED));
983}
984
985// Test that navigation is not blocked when we make new navigation before
986// previous one has been committed. This is also a regression test for
987// http://crbug.com/104600.
988TEST_F(RenderFrameHostManagerTest, NewCrossNavigationBetweenSwapOutAndCommit) {
989  const GURL kUrl1("http://www.google.com/");
990  const GURL kUrl2("http://www.chromium.org/");
991  const GURL kUrl3("http://www.youtube.com/");
992
993  contents()->NavigateAndCommit(kUrl1);
994  TestRenderViewHost* rvh1 = test_rvh();
995
996  // Keep active_view_count nonzero so that no swapped out views in
997  // this SiteInstance get forcefully deleted.
998  static_cast<SiteInstanceImpl*>(rvh1->GetSiteInstance())->
999      increment_active_view_count();
1000
1001  // Navigate but don't commit.
1002  contents()->GetController().LoadURL(
1003      kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
1004  EXPECT_TRUE(rvh1->is_waiting_for_beforeunload_ack());
1005  contents()->ProceedWithCrossSiteNavigation();
1006  EXPECT_FALSE(rvh1->is_waiting_for_beforeunload_ack());
1007  StartCrossSiteTransition(contents());
1008  EXPECT_TRUE(rvh1->IsWaitingForUnloadACK());
1009
1010  rvh1->OnSwappedOut(false);
1011  EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT, rvh1->rvh_state());
1012
1013  TestRenderViewHost* rvh2 = pending_test_rvh();
1014  EXPECT_TRUE(rvh2);
1015  static_cast<SiteInstanceImpl*>(rvh2->GetSiteInstance())->
1016      increment_active_view_count();
1017
1018  contents()->GetController().LoadURL(
1019      kUrl3, Referrer(), PAGE_TRANSITION_LINK, std::string());
1020  // Pending rvh2 is already deleted.
1021  contents()->ProceedWithCrossSiteNavigation();
1022
1023  TestRenderViewHost* rvh3 = pending_test_rvh();
1024  EXPECT_TRUE(rvh3);
1025  // Navigation should be already unblocked by rvh1.
1026  EXPECT_FALSE(rvh3->are_navigations_suspended());
1027}
1028
1029// Tests WebUI creation.
1030TEST_F(RenderFrameHostManagerTest, WebUI) {
1031  set_should_create_webui(true);
1032  SiteInstance* instance = SiteInstance::Create(browser_context());
1033
1034  scoped_ptr<TestWebContents> web_contents(
1035      TestWebContents::Create(browser_context(), instance));
1036  FrameTree tree(web_contents->GetFrameTree()->root()->navigator(),
1037                 web_contents.get(), web_contents.get(),
1038                 web_contents.get(), web_contents.get());
1039  RenderFrameHostManager* manager = tree.root()->render_manager();
1040
1041  manager->Init(browser_context(), instance, MSG_ROUTING_NONE,
1042                MSG_ROUTING_NONE);
1043  EXPECT_FALSE(manager->current_host()->IsRenderViewLive());
1044
1045  const GURL kUrl("chrome://foo");
1046  NavigationEntryImpl entry(NULL /* instance */, -1 /* page_id */, kUrl,
1047                            Referrer(), base::string16() /* title */,
1048                            PAGE_TRANSITION_TYPED,
1049                            false /* is_renderer_init */);
1050  RenderFrameHostImpl* host = manager->Navigate(entry);
1051
1052  // We commit the pending RenderFrameHost immediately because the previous
1053  // RenderFrameHost was not live.  We test a case where it is live in
1054  // WebUIInNewTab.
1055  EXPECT_TRUE(host);
1056  EXPECT_EQ(host, manager->current_frame_host());
1057  EXPECT_FALSE(manager->pending_frame_host());
1058
1059  // It's important that the site instance get set on the Web UI page as soon
1060  // as the navigation starts, rather than lazily after it commits, so we don't
1061  // try to re-use the SiteInstance/process for non Web UI things that may
1062  // get loaded in between.
1063  EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
1064      HasSite());
1065  EXPECT_EQ(kUrl, host->GetSiteInstance()->GetSiteURL());
1066
1067  // The Web UI is committed immediately because the RenderViewHost has not been
1068  // used yet. UpdateRendererStateForNavigate() took the short cut path.
1069  EXPECT_FALSE(manager->pending_web_ui());
1070  EXPECT_TRUE(manager->web_ui());
1071
1072  // Commit.
1073  manager->DidNavigateFrame(host);
1074  EXPECT_TRUE(
1075      host->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1076}
1077
1078// Tests that we can open a WebUI link in a new tab from a WebUI page and still
1079// grant the correct bindings.  http://crbug.com/189101.
1080TEST_F(RenderFrameHostManagerTest, WebUIInNewTab) {
1081  set_should_create_webui(true);
1082  SiteInstance* blank_instance = SiteInstance::Create(browser_context());
1083
1084  // Create a blank tab.
1085  scoped_ptr<TestWebContents> web_contents1(
1086      TestWebContents::Create(browser_context(), blank_instance));
1087  FrameTree tree1(web_contents1->GetFrameTree()->root()->navigator(),
1088                 web_contents1.get(), web_contents1.get(),
1089                 web_contents1.get(), web_contents1.get());
1090  RenderFrameHostManager* manager1 = tree1.root()->render_manager();
1091  manager1->Init(
1092      browser_context(), blank_instance, MSG_ROUTING_NONE, MSG_ROUTING_NONE);
1093  // Test the case that new RVH is considered live.
1094  manager1->current_host()->CreateRenderView(base::string16(), -1, -1);
1095
1096  // Navigate to a WebUI page.
1097  const GURL kUrl1("chrome://foo");
1098  NavigationEntryImpl entry1(NULL /* instance */, -1 /* page_id */, kUrl1,
1099                             Referrer(), base::string16() /* title */,
1100                             PAGE_TRANSITION_TYPED,
1101                             false /* is_renderer_init */);
1102  RenderFrameHostImpl* host1 = manager1->Navigate(entry1);
1103
1104  // We should have a pending navigation to the WebUI RenderViewHost.
1105  // It should already have bindings.
1106  EXPECT_EQ(host1, manager1->pending_frame_host());
1107  EXPECT_NE(host1, manager1->current_frame_host());
1108  EXPECT_TRUE(
1109      host1->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1110
1111  // Commit and ensure we still have bindings.
1112  manager1->DidNavigateFrame(host1);
1113  SiteInstance* webui_instance = host1->GetSiteInstance();
1114  EXPECT_EQ(host1, manager1->current_frame_host());
1115  EXPECT_TRUE(
1116      host1->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1117
1118  // Now simulate clicking a link that opens in a new tab.
1119  scoped_ptr<TestWebContents> web_contents2(
1120      TestWebContents::Create(browser_context(), webui_instance));
1121  FrameTree tree2(web_contents2->GetFrameTree()->root()->navigator(),
1122                  web_contents2.get(), web_contents2.get(),
1123                  web_contents2.get(), web_contents2.get());
1124  RenderFrameHostManager* manager2 = tree2.root()->render_manager();
1125  manager2->Init(
1126      browser_context(), webui_instance, MSG_ROUTING_NONE, MSG_ROUTING_NONE);
1127  // Make sure the new RVH is considered live.  This is usually done in
1128  // RenderWidgetHost::Init when opening a new tab from a link.
1129  manager2->current_host()->CreateRenderView(base::string16(), -1, -1);
1130
1131  const GURL kUrl2("chrome://foo/bar");
1132  NavigationEntryImpl entry2(NULL /* instance */, -1 /* page_id */, kUrl2,
1133                             Referrer(), base::string16() /* title */,
1134                             PAGE_TRANSITION_LINK,
1135                             true /* is_renderer_init */);
1136  RenderFrameHostImpl* host2 = manager2->Navigate(entry2);
1137
1138  // No cross-process transition happens because we are already in the right
1139  // SiteInstance.  We should grant bindings immediately.
1140  EXPECT_EQ(host2, manager2->current_frame_host());
1141  EXPECT_TRUE(
1142      host2->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1143
1144  manager2->DidNavigateFrame(host2);
1145}
1146
1147// Tests that we don't end up in an inconsistent state if a page does a back and
1148// then reload. http://crbug.com/51680
1149TEST_F(RenderFrameHostManagerTest, PageDoesBackAndReload) {
1150  const GURL kUrl1("http://www.google.com/");
1151  const GURL kUrl2("http://www.evil-site.com/");
1152
1153  // Navigate to a safe site, then an evil site.
1154  // This will switch RenderViewHosts.  We cannot assert that the first and
1155  // second RVHs are different, though, because the first one may be promptly
1156  // deleted.
1157  contents()->NavigateAndCommit(kUrl1);
1158  contents()->NavigateAndCommit(kUrl2);
1159  RenderViewHost* evil_rvh = contents()->GetRenderViewHost();
1160
1161  // Now let's simulate the evil page calling history.back().
1162  contents()->OnGoToEntryAtOffset(-1);
1163  // We should have a new pending RVH.
1164  // Note that in this case, the navigation has not committed, so evil_rvh will
1165  // not be deleted yet.
1166  EXPECT_NE(evil_rvh, contents()->GetRenderManagerForTesting()->
1167      pending_render_view_host());
1168
1169  // Before that RVH has committed, the evil page reloads itself.
1170  FrameHostMsg_DidCommitProvisionalLoad_Params params;
1171  params.page_id = 1;
1172  params.url = kUrl2;
1173  params.transition = PAGE_TRANSITION_CLIENT_REDIRECT;
1174  params.should_update_history = false;
1175  params.gesture = NavigationGestureAuto;
1176  params.was_within_same_page = false;
1177  params.is_post = false;
1178  params.page_state = PageState::CreateFromURL(kUrl2);
1179
1180  RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(evil_rvh);
1181  RenderFrameHostImpl* rfh = RenderFrameHostImpl::FromID(
1182      rvh->GetProcess()->GetID(), rvh->main_frame_routing_id());
1183  contents()->GetFrameTree()->root()->navigator()->DidNavigate(rfh, params);
1184
1185  // That should have cancelled the pending RVH, and the evil RVH should be the
1186  // current one.
1187  EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
1188      pending_render_view_host() == NULL);
1189  EXPECT_EQ(evil_rvh, contents()->GetRenderManagerForTesting()->current_host());
1190
1191  // Also we should not have a pending navigation entry.
1192  EXPECT_TRUE(contents()->GetController().GetPendingEntry() == NULL);
1193  NavigationEntry* entry = contents()->GetController().GetVisibleEntry();
1194  ASSERT_TRUE(entry != NULL);
1195  EXPECT_EQ(kUrl2, entry->GetURL());
1196}
1197
1198// Ensure that we can go back and forward even if a SwapOut ACK isn't received.
1199// See http://crbug.com/93427.
1200TEST_F(RenderFrameHostManagerTest, NavigateAfterMissingSwapOutACK) {
1201  const GURL kUrl1("http://www.google.com/");
1202  const GURL kUrl2("http://www.chromium.org/");
1203
1204  // Navigate to two pages.
1205  contents()->NavigateAndCommit(kUrl1);
1206  TestRenderViewHost* rvh1 = test_rvh();
1207
1208  // Keep active_view_count nonzero so that no swapped out views in
1209  // this SiteInstance get forcefully deleted.
1210  static_cast<SiteInstanceImpl*>(rvh1->GetSiteInstance())->
1211      increment_active_view_count();
1212
1213  contents()->NavigateAndCommit(kUrl2);
1214  TestRenderViewHost* rvh2 = test_rvh();
1215  static_cast<SiteInstanceImpl*>(rvh2->GetSiteInstance())->
1216      increment_active_view_count();
1217
1218  // Now go back, but suppose the SwapOut_ACK isn't received.  This shouldn't
1219  // happen, but we have seen it when going back quickly across many entries
1220  // (http://crbug.com/93427).
1221  contents()->GetController().GoBack();
1222  EXPECT_TRUE(rvh2->is_waiting_for_beforeunload_ack());
1223  contents()->ProceedWithCrossSiteNavigation();
1224  EXPECT_FALSE(rvh2->is_waiting_for_beforeunload_ack());
1225  StartCrossSiteTransition(contents());
1226  EXPECT_TRUE(rvh2->IsWaitingForUnloadACK());
1227
1228  // The back navigation commits.
1229  const NavigationEntry* entry1 = contents()->GetController().GetPendingEntry();
1230  rvh1->SendNavigate(entry1->GetPageID(), entry1->GetURL());
1231  EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT, rvh2->rvh_state());
1232
1233  // We should be able to navigate forward.
1234  contents()->GetController().GoForward();
1235  contents()->ProceedWithCrossSiteNavigation();
1236  StartCrossSiteTransition(contents());
1237  const NavigationEntry* entry2 = contents()->GetController().GetPendingEntry();
1238  rvh2->SendNavigate(entry2->GetPageID(), entry2->GetURL());
1239  EXPECT_EQ(rvh2, rvh());
1240  EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state());
1241  EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT, rvh1->rvh_state());
1242  rvh1->OnSwappedOut(false);
1243  EXPECT_TRUE(rvh1->IsSwappedOut());
1244}
1245
1246// Test that we create swapped out RVHs for the opener chain when navigating an
1247// opened tab cross-process.  This allows us to support certain cross-process
1248// JavaScript calls (http://crbug.com/99202).
1249TEST_F(RenderFrameHostManagerTest, CreateSwappedOutOpenerRVHs) {
1250  const GURL kUrl1("http://www.google.com/");
1251  const GURL kUrl2("http://www.chromium.org/");
1252  const GURL kChromeUrl("chrome://foo");
1253
1254  // Navigate to an initial URL.
1255  contents()->NavigateAndCommit(kUrl1);
1256  RenderFrameHostManager* manager = contents()->GetRenderManagerForTesting();
1257  TestRenderViewHost* rvh1 = test_rvh();
1258
1259  // Create 2 new tabs and simulate them being the opener chain for the main
1260  // tab.  They should be in the same SiteInstance.
1261  scoped_ptr<TestWebContents> opener1(
1262      TestWebContents::Create(browser_context(), rvh1->GetSiteInstance()));
1263  RenderFrameHostManager* opener1_manager =
1264      opener1->GetRenderManagerForTesting();
1265  contents()->SetOpener(opener1.get());
1266
1267  scoped_ptr<TestWebContents> opener2(
1268      TestWebContents::Create(browser_context(), rvh1->GetSiteInstance()));
1269  RenderFrameHostManager* opener2_manager =
1270      opener2->GetRenderManagerForTesting();
1271  opener1->SetOpener(opener2.get());
1272
1273  // Navigate to a cross-site URL (different SiteInstance but same
1274  // BrowsingInstance).
1275  contents()->NavigateAndCommit(kUrl2);
1276  TestRenderViewHost* rvh2 = test_rvh();
1277  EXPECT_NE(rvh1->GetSiteInstance(), rvh2->GetSiteInstance());
1278  EXPECT_TRUE(rvh1->GetSiteInstance()->IsRelatedSiteInstance(
1279                  rvh2->GetSiteInstance()));
1280
1281  // Ensure rvh1 is placed on swapped out list of the current tab.
1282  EXPECT_TRUE(manager->IsRVHOnSwappedOutList(rvh1));
1283  EXPECT_EQ(rvh1,
1284            manager->GetSwappedOutRenderViewHost(rvh1->GetSiteInstance()));
1285
1286  // Ensure a swapped out RVH is created in the first opener tab.
1287  TestRenderViewHost* opener1_rvh = static_cast<TestRenderViewHost*>(
1288      opener1_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance()));
1289  EXPECT_TRUE(opener1_manager->IsRVHOnSwappedOutList(opener1_rvh));
1290  EXPECT_TRUE(opener1_rvh->IsSwappedOut());
1291
1292  // Ensure a swapped out RVH is created in the second opener tab.
1293  TestRenderViewHost* opener2_rvh = static_cast<TestRenderViewHost*>(
1294      opener2_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance()));
1295  EXPECT_TRUE(opener2_manager->IsRVHOnSwappedOutList(opener2_rvh));
1296  EXPECT_TRUE(opener2_rvh->IsSwappedOut());
1297
1298  // Navigate to a cross-BrowsingInstance URL.
1299  contents()->NavigateAndCommit(kChromeUrl);
1300  TestRenderViewHost* rvh3 = test_rvh();
1301  EXPECT_NE(rvh1->GetSiteInstance(), rvh3->GetSiteInstance());
1302  EXPECT_FALSE(rvh1->GetSiteInstance()->IsRelatedSiteInstance(
1303                   rvh3->GetSiteInstance()));
1304
1305  // No scripting is allowed across BrowsingInstances, so we should not create
1306  // swapped out RVHs for the opener chain in this case.
1307  EXPECT_FALSE(opener1_manager->GetSwappedOutRenderViewHost(
1308                   rvh3->GetSiteInstance()));
1309  EXPECT_FALSE(opener2_manager->GetSwappedOutRenderViewHost(
1310                   rvh3->GetSiteInstance()));
1311}
1312
1313// Test that we clean up swapped out RenderViewHosts when a process hosting
1314// those associated RenderViews crashes. http://crbug.com/258993
1315TEST_F(RenderFrameHostManagerTest, CleanUpSwappedOutRVHOnProcessCrash) {
1316  const GURL kUrl1("http://www.google.com/");
1317  const GURL kUrl2("http://www.chromium.org/");
1318
1319  // Navigate to an initial URL.
1320  contents()->NavigateAndCommit(kUrl1);
1321  TestRenderViewHost* rvh1 = test_rvh();
1322
1323  // Create a new tab as an opener for the main tab.
1324  scoped_ptr<TestWebContents> opener1(
1325      TestWebContents::Create(browser_context(), rvh1->GetSiteInstance()));
1326  RenderFrameHostManager* opener1_manager =
1327      opener1->GetRenderManagerForTesting();
1328  contents()->SetOpener(opener1.get());
1329
1330  // Make sure the new opener RVH is considered live.
1331  opener1_manager->current_host()->CreateRenderView(base::string16(), -1, -1);
1332
1333  // Use a cross-process navigation in the opener to swap out the old RVH.
1334  EXPECT_FALSE(opener1_manager->GetSwappedOutRenderViewHost(
1335      rvh1->GetSiteInstance()));
1336  opener1->NavigateAndCommit(kUrl2);
1337  EXPECT_TRUE(opener1_manager->GetSwappedOutRenderViewHost(
1338      rvh1->GetSiteInstance()));
1339
1340  // Fake a process crash.
1341  RenderProcessHost::RendererClosedDetails details(
1342      rvh1->GetProcess()->GetHandle(),
1343      base::TERMINATION_STATUS_PROCESS_CRASHED,
1344      0);
1345  NotificationService::current()->Notify(
1346      NOTIFICATION_RENDERER_PROCESS_CLOSED,
1347      Source<RenderProcessHost>(rvh1->GetProcess()),
1348      Details<RenderProcessHost::RendererClosedDetails>(&details));
1349  rvh1->set_render_view_created(false);
1350
1351  // Ensure that the swapped out RenderViewHost has been deleted.
1352  EXPECT_FALSE(opener1_manager->GetSwappedOutRenderViewHost(
1353      rvh1->GetSiteInstance()));
1354
1355  // Reload the initial tab. This should recreate the opener's swapped out RVH
1356  // in the original SiteInstance.
1357  contents()->GetController().Reload(true);
1358  EXPECT_EQ(opener1_manager->GetSwappedOutRenderViewHost(
1359                rvh1->GetSiteInstance())->GetRoutingID(),
1360            test_rvh()->opener_route_id());
1361}
1362
1363// Test that RenderViewHosts created for WebUI navigations are properly
1364// granted WebUI bindings even if an unprivileged swapped out RenderViewHost
1365// is in the same process (http://crbug.com/79918).
1366TEST_F(RenderFrameHostManagerTest, EnableWebUIWithSwappedOutOpener) {
1367  set_should_create_webui(true);
1368  const GURL kSettingsUrl("chrome://chrome/settings");
1369  const GURL kPluginUrl("chrome://plugins");
1370
1371  // Navigate to an initial WebUI URL.
1372  contents()->NavigateAndCommit(kSettingsUrl);
1373
1374  // Ensure the RVH has WebUI bindings.
1375  TestRenderViewHost* rvh1 = test_rvh();
1376  EXPECT_TRUE(rvh1->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1377
1378  // Create a new tab and simulate it being the opener for the main
1379  // tab.  It should be in the same SiteInstance.
1380  scoped_ptr<TestWebContents> opener1(
1381      TestWebContents::Create(browser_context(), rvh1->GetSiteInstance()));
1382  RenderFrameHostManager* opener1_manager =
1383      opener1->GetRenderManagerForTesting();
1384  contents()->SetOpener(opener1.get());
1385
1386  // Navigate to a different WebUI URL (different SiteInstance, same
1387  // BrowsingInstance).
1388  contents()->NavigateAndCommit(kPluginUrl);
1389  TestRenderViewHost* rvh2 = test_rvh();
1390  EXPECT_NE(rvh1->GetSiteInstance(), rvh2->GetSiteInstance());
1391  EXPECT_TRUE(rvh1->GetSiteInstance()->IsRelatedSiteInstance(
1392                  rvh2->GetSiteInstance()));
1393
1394  // Ensure a swapped out RVH is created in the first opener tab.
1395  TestRenderViewHost* opener1_rvh = static_cast<TestRenderViewHost*>(
1396      opener1_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance()));
1397  EXPECT_TRUE(opener1_manager->IsRVHOnSwappedOutList(opener1_rvh));
1398  EXPECT_TRUE(opener1_rvh->IsSwappedOut());
1399
1400  // Ensure the new RVH has WebUI bindings.
1401  EXPECT_TRUE(rvh2->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1402}
1403
1404// Test that we reuse the same guest SiteInstance if we navigate across sites.
1405TEST_F(RenderFrameHostManagerTest, NoSwapOnGuestNavigations) {
1406  TestNotificationTracker notifications;
1407
1408  GURL guest_url(std::string(kGuestScheme).append("://abc123"));
1409  SiteInstance* instance =
1410      SiteInstance::CreateForURL(browser_context(), guest_url);
1411  scoped_ptr<TestWebContents> web_contents(
1412      TestWebContents::Create(browser_context(), instance));
1413
1414  // Create.
1415  FrameTree tree(web_contents->GetFrameTree()->root()->navigator(),
1416                 web_contents.get(), web_contents.get(),
1417                 web_contents.get(), web_contents.get());
1418  RenderFrameHostManager* manager = tree.root()->render_manager();
1419
1420  manager->Init(browser_context(), instance, MSG_ROUTING_NONE,
1421                MSG_ROUTING_NONE);
1422
1423  RenderFrameHostImpl* host;
1424
1425  // 1) The first navigation. --------------------------
1426  const GURL kUrl1("http://www.google.com/");
1427  NavigationEntryImpl entry1(
1428      NULL /* instance */, -1 /* page_id */, kUrl1, Referrer(),
1429      base::string16() /* title */, PAGE_TRANSITION_TYPED,
1430      false /* is_renderer_init */);
1431  host = manager->Navigate(entry1);
1432
1433  // The RenderFrameHost created in Init will be reused.
1434  EXPECT_TRUE(host == manager->current_frame_host());
1435  EXPECT_FALSE(manager->pending_frame_host());
1436  EXPECT_EQ(manager->current_frame_host()->GetSiteInstance(), instance);
1437
1438  // Commit.
1439  manager->DidNavigateFrame(host);
1440  // Commit to SiteInstance should be delayed until RenderView commit.
1441  EXPECT_EQ(host, manager->current_frame_host());
1442  ASSERT_TRUE(host);
1443  EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
1444      HasSite());
1445
1446  // 2) Navigate to a different domain. -------------------------
1447  // Guests stay in the same process on navigation.
1448  const GURL kUrl2("http://www.chromium.org");
1449  NavigationEntryImpl entry2(
1450      NULL /* instance */, -1 /* page_id */, kUrl2,
1451      Referrer(kUrl1, blink::WebReferrerPolicyDefault),
1452      base::string16() /* title */, PAGE_TRANSITION_LINK,
1453      true /* is_renderer_init */);
1454  host = manager->Navigate(entry2);
1455
1456  // The RenderFrameHost created in Init will be reused.
1457  EXPECT_EQ(host, manager->current_frame_host());
1458  EXPECT_FALSE(manager->pending_frame_host());
1459
1460  // Commit.
1461  manager->DidNavigateFrame(host);
1462  EXPECT_EQ(host, manager->current_frame_host());
1463  ASSERT_TRUE(host);
1464  EXPECT_EQ(static_cast<SiteInstanceImpl*>(host->GetSiteInstance()),
1465      instance);
1466}
1467
1468// Test that we cancel a pending RVH if we close the tab while it's pending.
1469// http://crbug.com/294697.
1470TEST_F(RenderFrameHostManagerTest, NavigateWithEarlyClose) {
1471  TestNotificationTracker notifications;
1472
1473  SiteInstance* instance = SiteInstance::Create(browser_context());
1474
1475  BeforeUnloadFiredWebContentsDelegate delegate;
1476  scoped_ptr<TestWebContents> web_contents(
1477      TestWebContents::Create(browser_context(), instance));
1478  web_contents->SetDelegate(&delegate);
1479  notifications.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
1480                          Source<WebContents>(web_contents.get()));
1481
1482  // Create.
1483  FrameTree tree(web_contents->GetFrameTree()->root()->navigator(),
1484                 web_contents.get(), web_contents.get(),
1485                 web_contents.get(), web_contents.get());
1486  RenderFrameHostManager* manager = tree.root()->render_manager();
1487
1488  manager->Init(browser_context(), instance, MSG_ROUTING_NONE,
1489                MSG_ROUTING_NONE);
1490
1491  // 1) The first navigation. --------------------------
1492  const GURL kUrl1("http://www.google.com/");
1493  NavigationEntryImpl entry1(NULL /* instance */, -1 /* page_id */, kUrl1,
1494                             Referrer(), base::string16() /* title */,
1495                             PAGE_TRANSITION_TYPED,
1496                             false /* is_renderer_init */);
1497  RenderFrameHostImpl* host = manager->Navigate(entry1);
1498
1499  // The RenderFrameHost created in Init will be reused.
1500  EXPECT_EQ(host, manager->current_frame_host());
1501  EXPECT_FALSE(manager->pending_frame_host());
1502
1503  // We should observe a notification.
1504  EXPECT_TRUE(
1505      notifications.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED));
1506  notifications.Reset();
1507
1508  // Commit.
1509  manager->DidNavigateFrame(host);
1510
1511  // Commit to SiteInstance should be delayed until RenderFrame commits.
1512  EXPECT_EQ(host, manager->current_frame_host());
1513  EXPECT_FALSE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
1514      HasSite());
1515  static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->SetSite(kUrl1);
1516
1517  // 2) Cross-site navigate to next site. -------------------------
1518  const GURL kUrl2("http://www.example.com");
1519  NavigationEntryImpl entry2(
1520      NULL /* instance */, -1 /* page_id */, kUrl2, Referrer(),
1521      base::string16() /* title */, PAGE_TRANSITION_TYPED,
1522      false /* is_renderer_init */);
1523  RenderFrameHostImpl* host2 = manager->Navigate(entry2);
1524
1525  // A new RenderFrameHost should be created.
1526  ASSERT_EQ(host2, manager->pending_frame_host());
1527  EXPECT_NE(host2, host);
1528
1529  EXPECT_EQ(host, manager->current_frame_host());
1530  EXPECT_FALSE(manager->current_frame_host()->is_swapped_out());
1531  EXPECT_EQ(host2, manager->pending_frame_host());
1532
1533  // 3) Close the tab. -------------------------
1534  notifications.ListenFor(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
1535                          Source<RenderWidgetHost>(host2->render_view_host()));
1536  manager->OnBeforeUnloadACK(false, true, base::TimeTicks());
1537
1538  EXPECT_TRUE(
1539      notifications.Check1AndReset(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED));
1540  EXPECT_FALSE(manager->pending_frame_host());
1541  EXPECT_EQ(host, manager->current_frame_host());
1542}
1543
1544// Tests that the RenderViewHost is properly deleted when the SwapOutACK is
1545// received before the new page commits.
1546TEST_F(RenderFrameHostManagerTest,
1547       SwapOutACKBeforeNewPageCommitsLeadsToDeletion) {
1548  const GURL kUrl1("http://www.google.com/");
1549  const GURL kUrl2("http://www.chromium.org/");
1550
1551  // Navigate to the first page.
1552  contents()->NavigateAndCommit(kUrl1);
1553  TestRenderViewHost* rvh1 = test_rvh();
1554  RenderViewHostDeletedObserver rvh_deleted_observer(rvh1);
1555  EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
1556
1557  // Navigate to new site, simulating onbeforeunload approval.
1558  controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
1559  base::TimeTicks now = base::TimeTicks::Now();
1560  main_test_rfh()->OnMessageReceived(
1561      FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
1562  EXPECT_TRUE(contents()->cross_navigation_pending());
1563  TestRenderViewHost* rvh2 =
1564      static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
1565
1566  // Simulate rvh2's response, which leads to an unload request being sent to
1567  // rvh1.
1568  std::vector<GURL> url_chain;
1569  url_chain.push_back(GURL());
1570  contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
1571      contents()->GetRenderManagerForTesting()->pending_frame_host(),
1572      GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
1573      url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
1574  EXPECT_TRUE(contents()->cross_navigation_pending());
1575  EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK,
1576            rvh1->rvh_state());
1577
1578  // Simulate the swap out ack.
1579  rvh1->OnSwappedOut(false);
1580  EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT, rvh1->rvh_state());
1581
1582  // The new page commits.
1583  contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED);
1584  EXPECT_FALSE(contents()->cross_navigation_pending());
1585  EXPECT_EQ(rvh2, rvh());
1586  EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
1587  EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state());
1588
1589  // rvh1 should have been deleted.
1590  EXPECT_TRUE(rvh_deleted_observer.deleted());
1591  rvh1 = NULL;
1592}
1593
1594// Tests that the RenderViewHost is properly swapped out when the SwapOutACK is
1595// received before the new page commits.
1596TEST_F(RenderFrameHostManagerTest,
1597       SwapOutACKBeforeNewPageCommitsLeadsToSwapOut) {
1598  const GURL kUrl1("http://www.google.com/");
1599  const GURL kUrl2("http://www.chromium.org/");
1600
1601  // Navigate to the first page.
1602  contents()->NavigateAndCommit(kUrl1);
1603  TestRenderViewHost* rvh1 = test_rvh();
1604  RenderViewHostDeletedObserver rvh_deleted_observer(rvh1);
1605  EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
1606
1607  // Increment the number of active views in SiteInstanceImpl so that rvh2 is
1608  // not deleted on swap out.
1609  static_cast<SiteInstanceImpl*>(
1610      rvh1->GetSiteInstance())->increment_active_view_count();
1611
1612  // Navigate to new site, simulating onbeforeunload approval.
1613  controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
1614  base::TimeTicks now = base::TimeTicks::Now();
1615  main_test_rfh()->OnMessageReceived(
1616      FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
1617  EXPECT_TRUE(contents()->cross_navigation_pending());
1618  TestRenderViewHost* rvh2 =
1619      static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
1620
1621  // Simulate rvh2's response, which leads to an unload request being sent to
1622  // rvh1.
1623  std::vector<GURL> url_chain;
1624  url_chain.push_back(GURL());
1625  contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
1626      contents()->GetRenderManagerForTesting()->pending_frame_host(),
1627      GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
1628      url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
1629  EXPECT_TRUE(contents()->cross_navigation_pending());
1630  EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK,
1631            rvh1->rvh_state());
1632
1633  // Simulate the swap out ack.
1634  rvh1->OnSwappedOut(false);
1635  EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT, rvh1->rvh_state());
1636
1637  // The new page commits.
1638  contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED);
1639  EXPECT_FALSE(contents()->cross_navigation_pending());
1640  EXPECT_EQ(rvh2, rvh());
1641  EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
1642  EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state());
1643
1644  // rvh1 should be swapped out.
1645  EXPECT_FALSE(rvh_deleted_observer.deleted());
1646  EXPECT_TRUE(rvh1->IsSwappedOut());
1647}
1648
1649// Tests that the RenderViewHost is properly deleted when the new
1650// page commits before the swap out ack is received.
1651TEST_F(RenderFrameHostManagerTest,
1652       NewPageCommitsBeforeSwapOutACKLeadsToDeletion) {
1653  const GURL kUrl1("http://www.google.com/");
1654  const GURL kUrl2("http://www.chromium.org/");
1655
1656  // Navigate to the first page.
1657  contents()->NavigateAndCommit(kUrl1);
1658  TestRenderViewHost* rvh1 = test_rvh();
1659  RenderViewHostDeletedObserver rvh_deleted_observer(rvh1);
1660  EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
1661
1662  // Navigate to new site, simulating onbeforeunload approval.
1663  controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
1664  base::TimeTicks now = base::TimeTicks::Now();
1665  main_test_rfh()->OnMessageReceived(
1666      FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
1667  EXPECT_TRUE(contents()->cross_navigation_pending());
1668  TestRenderViewHost* rvh2 =
1669      static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
1670
1671  // Simulate rvh2's response, which leads to an unload request being sent to
1672  // rvh1.
1673  std::vector<GURL> url_chain;
1674  url_chain.push_back(GURL());
1675  contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
1676      contents()->GetRenderManagerForTesting()->pending_frame_host(),
1677      GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
1678      url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
1679  EXPECT_TRUE(contents()->cross_navigation_pending());
1680  EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK,
1681            rvh1->rvh_state());
1682
1683  // The new page commits.
1684  contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED);
1685  EXPECT_FALSE(contents()->cross_navigation_pending());
1686  EXPECT_EQ(rvh2, rvh());
1687  EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
1688  EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state());
1689  EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SHUTDOWN, rvh1->rvh_state());
1690
1691  // Simulate the swap out ack.
1692  rvh1->OnSwappedOut(false);
1693
1694  // rvh1 should have been deleted.
1695  EXPECT_TRUE(rvh_deleted_observer.deleted());
1696  rvh1 = NULL;
1697}
1698
1699// Tests that the RenderViewHost is properly swapped out when the new page
1700// commits before the swap out ack is received.
1701TEST_F(RenderFrameHostManagerTest,
1702       NewPageCommitsBeforeSwapOutACKLeadsToSwapOut) {
1703  const GURL kUrl1("http://www.google.com/");
1704  const GURL kUrl2("http://www.chromium.org/");
1705
1706  // Navigate to the first page.
1707  contents()->NavigateAndCommit(kUrl1);
1708  TestRenderViewHost* rvh1 = test_rvh();
1709  RenderViewHostDeletedObserver rvh_deleted_observer(rvh1);
1710  EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
1711
1712  // Increment the number of active views in SiteInstanceImpl so that rvh1 is
1713  // not deleted on swap out.
1714  static_cast<SiteInstanceImpl*>(
1715      rvh1->GetSiteInstance())->increment_active_view_count();
1716
1717  // Navigate to new site, simulating onbeforeunload approval.
1718  controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
1719  base::TimeTicks now = base::TimeTicks::Now();
1720  main_test_rfh()->OnMessageReceived(
1721      FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
1722  EXPECT_TRUE(contents()->cross_navigation_pending());
1723  TestRenderViewHost* rvh2 =
1724      static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
1725
1726  // Simulate rvh2's response, which leads to an unload request being sent to
1727  // rvh1.
1728  std::vector<GURL> url_chain;
1729  url_chain.push_back(GURL());
1730  contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
1731      contents()->GetRenderManagerForTesting()->pending_frame_host(),
1732      GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
1733      url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
1734  EXPECT_TRUE(contents()->cross_navigation_pending());
1735  EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK,
1736            rvh1->rvh_state());
1737
1738  // The new page commits.
1739  contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED);
1740  EXPECT_FALSE(contents()->cross_navigation_pending());
1741  EXPECT_EQ(rvh2, rvh());
1742  EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
1743  EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state());
1744  EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT, rvh1->rvh_state());
1745
1746  // Simulate the swap out ack.
1747  rvh1->OnSwappedOut(false);
1748
1749  // rvh1 should be swapped out.
1750  EXPECT_FALSE(rvh_deleted_observer.deleted());
1751  EXPECT_TRUE(rvh1->IsSwappedOut());
1752}
1753
1754// Test that the RenderViewHost is properly swapped out if a navigation in the
1755// new renderer commits before sending the SwapOut message to the old renderer.
1756// This simulates a cross-site navigation to a synchronously committing URL
1757// (e.g., a data URL) and ensures it works properly.
1758TEST_F(RenderFrameHostManagerTest,
1759       CommitNewNavigationBeforeSendingSwapOut) {
1760  const GURL kUrl1("http://www.google.com/");
1761  const GURL kUrl2("http://www.chromium.org/");
1762
1763  // Navigate to the first page.
1764  contents()->NavigateAndCommit(kUrl1);
1765  TestRenderViewHost* rvh1 = test_rvh();
1766  RenderViewHostDeletedObserver rvh_deleted_observer(rvh1);
1767  EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
1768
1769  // Increment the number of active views in SiteInstanceImpl so that rvh1 is
1770  // not deleted on swap out.
1771  static_cast<SiteInstanceImpl*>(
1772      rvh1->GetSiteInstance())->increment_active_view_count();
1773
1774  // Navigate to new site, simulating onbeforeunload approval.
1775  controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
1776  base::TimeTicks now = base::TimeTicks::Now();
1777  main_test_rfh()->OnMessageReceived(
1778      FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
1779  EXPECT_TRUE(contents()->cross_navigation_pending());
1780  TestRenderViewHost* rvh2 =
1781      static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
1782
1783  // The new page commits.
1784  contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED);
1785  EXPECT_FALSE(contents()->cross_navigation_pending());
1786  EXPECT_EQ(rvh2, rvh());
1787  EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
1788  EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state());
1789  EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT, rvh1->rvh_state());
1790
1791  // Simulate the swap out ack.
1792  rvh1->OnSwappedOut(false);
1793
1794  // rvh1 should be swapped out.
1795  EXPECT_FALSE(rvh_deleted_observer.deleted());
1796  EXPECT_TRUE(rvh1->IsSwappedOut());
1797}
1798
1799// Test that a RenderFrameHost is properly deleted or swapped out when a
1800// cross-site navigation is cancelled.
1801TEST_F(RenderFrameHostManagerTest,
1802       CancelPendingProperlyDeletesOrSwaps) {
1803  const GURL kUrl1("http://www.google.com/");
1804  const GURL kUrl2("http://www.chromium.org/");
1805  RenderFrameHostImpl* pending_rfh = NULL;
1806  base::TimeTicks now = base::TimeTicks::Now();
1807
1808  // Navigate to the first page.
1809  contents()->NavigateAndCommit(kUrl1);
1810  TestRenderViewHost* rvh1 = test_rvh();
1811  EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
1812
1813  // Navigate to a new site, starting a cross-site navigation.
1814  controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
1815  {
1816    pending_rfh = contents()->GetFrameTree()->root()->render_manager()
1817        ->pending_frame_host();
1818    RenderFrameHostDeletedObserver rvh_deleted_observer(pending_rfh);
1819
1820    // Cancel the navigation by simulating a declined beforeunload dialog.
1821    main_test_rfh()->OnMessageReceived(
1822        FrameHostMsg_BeforeUnload_ACK(0, false, now, now));
1823    EXPECT_FALSE(contents()->cross_navigation_pending());
1824
1825    // Since the pending RFH is the only one for the new SiteInstance, it should
1826    // be deleted.
1827    EXPECT_TRUE(rvh_deleted_observer.deleted());
1828  }
1829
1830  // Start another cross-site navigation.
1831  controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
1832  {
1833    pending_rfh = contents()->GetFrameTree()->root()->render_manager()
1834        ->pending_frame_host();
1835    RenderFrameHostDeletedObserver rvh_deleted_observer(pending_rfh);
1836
1837    // Increment the number of active views in the new SiteInstance, which will
1838    // cause the pending RFH to be swapped out instead of deleted.
1839    static_cast<SiteInstanceImpl*>(
1840        pending_rfh->GetSiteInstance())->increment_active_view_count();
1841
1842    main_test_rfh()->OnMessageReceived(
1843        FrameHostMsg_BeforeUnload_ACK(0, false, now, now));
1844    EXPECT_FALSE(contents()->cross_navigation_pending());
1845    EXPECT_FALSE(rvh_deleted_observer.deleted());
1846  }
1847}
1848
1849}  // namespace content
1850