render_frame_host_manager_unittest.cc revision 0529e5d033099cbfc42635f6f6183833b09dff6e
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, false);
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, false);
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(
1332      base::string16(), -1, -1, false);
1333
1334  // Use a cross-process navigation in the opener to swap out the old RVH.
1335  EXPECT_FALSE(opener1_manager->GetSwappedOutRenderViewHost(
1336      rvh1->GetSiteInstance()));
1337  opener1->NavigateAndCommit(kUrl2);
1338  EXPECT_TRUE(opener1_manager->GetSwappedOutRenderViewHost(
1339      rvh1->GetSiteInstance()));
1340
1341  // Fake a process crash.
1342  RenderProcessHost::RendererClosedDetails details(
1343      rvh1->GetProcess()->GetHandle(),
1344      base::TERMINATION_STATUS_PROCESS_CRASHED,
1345      0);
1346  NotificationService::current()->Notify(
1347      NOTIFICATION_RENDERER_PROCESS_CLOSED,
1348      Source<RenderProcessHost>(rvh1->GetProcess()),
1349      Details<RenderProcessHost::RendererClosedDetails>(&details));
1350  rvh1->set_render_view_created(false);
1351
1352  // Ensure that the swapped out RenderViewHost has been deleted.
1353  EXPECT_FALSE(opener1_manager->GetSwappedOutRenderViewHost(
1354      rvh1->GetSiteInstance()));
1355
1356  // Reload the initial tab. This should recreate the opener's swapped out RVH
1357  // in the original SiteInstance.
1358  contents()->GetController().Reload(true);
1359  EXPECT_EQ(opener1_manager->GetSwappedOutRenderViewHost(
1360                rvh1->GetSiteInstance())->GetRoutingID(),
1361            test_rvh()->opener_route_id());
1362}
1363
1364// Test that RenderViewHosts created for WebUI navigations are properly
1365// granted WebUI bindings even if an unprivileged swapped out RenderViewHost
1366// is in the same process (http://crbug.com/79918).
1367TEST_F(RenderFrameHostManagerTest, EnableWebUIWithSwappedOutOpener) {
1368  set_should_create_webui(true);
1369  const GURL kSettingsUrl("chrome://chrome/settings");
1370  const GURL kPluginUrl("chrome://plugins");
1371
1372  // Navigate to an initial WebUI URL.
1373  contents()->NavigateAndCommit(kSettingsUrl);
1374
1375  // Ensure the RVH has WebUI bindings.
1376  TestRenderViewHost* rvh1 = test_rvh();
1377  EXPECT_TRUE(rvh1->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1378
1379  // Create a new tab and simulate it being the opener for the main
1380  // tab.  It should be in the same SiteInstance.
1381  scoped_ptr<TestWebContents> opener1(
1382      TestWebContents::Create(browser_context(), rvh1->GetSiteInstance()));
1383  RenderFrameHostManager* opener1_manager =
1384      opener1->GetRenderManagerForTesting();
1385  contents()->SetOpener(opener1.get());
1386
1387  // Navigate to a different WebUI URL (different SiteInstance, same
1388  // BrowsingInstance).
1389  contents()->NavigateAndCommit(kPluginUrl);
1390  TestRenderViewHost* rvh2 = test_rvh();
1391  EXPECT_NE(rvh1->GetSiteInstance(), rvh2->GetSiteInstance());
1392  EXPECT_TRUE(rvh1->GetSiteInstance()->IsRelatedSiteInstance(
1393                  rvh2->GetSiteInstance()));
1394
1395  // Ensure a swapped out RVH is created in the first opener tab.
1396  TestRenderViewHost* opener1_rvh = static_cast<TestRenderViewHost*>(
1397      opener1_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance()));
1398  EXPECT_TRUE(opener1_manager->IsRVHOnSwappedOutList(opener1_rvh));
1399  EXPECT_TRUE(opener1_rvh->IsSwappedOut());
1400
1401  // Ensure the new RVH has WebUI bindings.
1402  EXPECT_TRUE(rvh2->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1403}
1404
1405// Test that we reuse the same guest SiteInstance if we navigate across sites.
1406TEST_F(RenderFrameHostManagerTest, NoSwapOnGuestNavigations) {
1407  TestNotificationTracker notifications;
1408
1409  GURL guest_url(std::string(kGuestScheme).append("://abc123"));
1410  SiteInstance* instance =
1411      SiteInstance::CreateForURL(browser_context(), guest_url);
1412  scoped_ptr<TestWebContents> web_contents(
1413      TestWebContents::Create(browser_context(), instance));
1414
1415  // Create.
1416  FrameTree tree(web_contents->GetFrameTree()->root()->navigator(),
1417                 web_contents.get(), web_contents.get(),
1418                 web_contents.get(), web_contents.get());
1419  RenderFrameHostManager* manager = tree.root()->render_manager();
1420
1421  manager->Init(browser_context(), instance, MSG_ROUTING_NONE,
1422                MSG_ROUTING_NONE);
1423
1424  RenderFrameHostImpl* host;
1425
1426  // 1) The first navigation. --------------------------
1427  const GURL kUrl1("http://www.google.com/");
1428  NavigationEntryImpl entry1(
1429      NULL /* instance */, -1 /* page_id */, kUrl1, Referrer(),
1430      base::string16() /* title */, PAGE_TRANSITION_TYPED,
1431      false /* is_renderer_init */);
1432  host = manager->Navigate(entry1);
1433
1434  // The RenderFrameHost created in Init will be reused.
1435  EXPECT_TRUE(host == manager->current_frame_host());
1436  EXPECT_FALSE(manager->pending_frame_host());
1437  EXPECT_EQ(manager->current_frame_host()->GetSiteInstance(), instance);
1438
1439  // Commit.
1440  manager->DidNavigateFrame(host);
1441  // Commit to SiteInstance should be delayed until RenderView commit.
1442  EXPECT_EQ(host, manager->current_frame_host());
1443  ASSERT_TRUE(host);
1444  EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
1445      HasSite());
1446
1447  // 2) Navigate to a different domain. -------------------------
1448  // Guests stay in the same process on navigation.
1449  const GURL kUrl2("http://www.chromium.org");
1450  NavigationEntryImpl entry2(
1451      NULL /* instance */, -1 /* page_id */, kUrl2,
1452      Referrer(kUrl1, blink::WebReferrerPolicyDefault),
1453      base::string16() /* title */, PAGE_TRANSITION_LINK,
1454      true /* is_renderer_init */);
1455  host = manager->Navigate(entry2);
1456
1457  // The RenderFrameHost created in Init will be reused.
1458  EXPECT_EQ(host, manager->current_frame_host());
1459  EXPECT_FALSE(manager->pending_frame_host());
1460
1461  // Commit.
1462  manager->DidNavigateFrame(host);
1463  EXPECT_EQ(host, manager->current_frame_host());
1464  ASSERT_TRUE(host);
1465  EXPECT_EQ(static_cast<SiteInstanceImpl*>(host->GetSiteInstance()),
1466      instance);
1467}
1468
1469// Test that we cancel a pending RVH if we close the tab while it's pending.
1470// http://crbug.com/294697.
1471TEST_F(RenderFrameHostManagerTest, NavigateWithEarlyClose) {
1472  TestNotificationTracker notifications;
1473
1474  SiteInstance* instance = SiteInstance::Create(browser_context());
1475
1476  BeforeUnloadFiredWebContentsDelegate delegate;
1477  scoped_ptr<TestWebContents> web_contents(
1478      TestWebContents::Create(browser_context(), instance));
1479  web_contents->SetDelegate(&delegate);
1480  notifications.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
1481                          Source<WebContents>(web_contents.get()));
1482
1483  // Create.
1484  FrameTree tree(web_contents->GetFrameTree()->root()->navigator(),
1485                 web_contents.get(), web_contents.get(),
1486                 web_contents.get(), web_contents.get());
1487  RenderFrameHostManager* manager = tree.root()->render_manager();
1488
1489  manager->Init(browser_context(), instance, MSG_ROUTING_NONE,
1490                MSG_ROUTING_NONE);
1491
1492  // 1) The first navigation. --------------------------
1493  const GURL kUrl1("http://www.google.com/");
1494  NavigationEntryImpl entry1(NULL /* instance */, -1 /* page_id */, kUrl1,
1495                             Referrer(), base::string16() /* title */,
1496                             PAGE_TRANSITION_TYPED,
1497                             false /* is_renderer_init */);
1498  RenderFrameHostImpl* host = manager->Navigate(entry1);
1499
1500  // The RenderFrameHost created in Init will be reused.
1501  EXPECT_EQ(host, manager->current_frame_host());
1502  EXPECT_FALSE(manager->pending_frame_host());
1503
1504  // We should observe a notification.
1505  EXPECT_TRUE(
1506      notifications.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED));
1507  notifications.Reset();
1508
1509  // Commit.
1510  manager->DidNavigateFrame(host);
1511
1512  // Commit to SiteInstance should be delayed until RenderFrame commits.
1513  EXPECT_EQ(host, manager->current_frame_host());
1514  EXPECT_FALSE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
1515      HasSite());
1516  static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->SetSite(kUrl1);
1517
1518  // 2) Cross-site navigate to next site. -------------------------
1519  const GURL kUrl2("http://www.example.com");
1520  NavigationEntryImpl entry2(
1521      NULL /* instance */, -1 /* page_id */, kUrl2, Referrer(),
1522      base::string16() /* title */, PAGE_TRANSITION_TYPED,
1523      false /* is_renderer_init */);
1524  RenderFrameHostImpl* host2 = manager->Navigate(entry2);
1525
1526  // A new RenderFrameHost should be created.
1527  ASSERT_EQ(host2, manager->pending_frame_host());
1528  EXPECT_NE(host2, host);
1529
1530  EXPECT_EQ(host, manager->current_frame_host());
1531  EXPECT_FALSE(manager->current_frame_host()->is_swapped_out());
1532  EXPECT_EQ(host2, manager->pending_frame_host());
1533
1534  // 3) Close the tab. -------------------------
1535  notifications.ListenFor(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
1536                          Source<RenderWidgetHost>(host2->render_view_host()));
1537  manager->OnBeforeUnloadACK(false, true, base::TimeTicks());
1538
1539  EXPECT_TRUE(
1540      notifications.Check1AndReset(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED));
1541  EXPECT_FALSE(manager->pending_frame_host());
1542  EXPECT_EQ(host, manager->current_frame_host());
1543}
1544
1545// Tests that the RenderViewHost is properly deleted when the SwapOutACK is
1546// received before the new page commits.
1547TEST_F(RenderFrameHostManagerTest,
1548       SwapOutACKBeforeNewPageCommitsLeadsToDeletion) {
1549  const GURL kUrl1("http://www.google.com/");
1550  const GURL kUrl2("http://www.chromium.org/");
1551
1552  // Navigate to the first page.
1553  contents()->NavigateAndCommit(kUrl1);
1554  TestRenderViewHost* rvh1 = test_rvh();
1555  RenderViewHostDeletedObserver rvh_deleted_observer(rvh1);
1556  EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
1557
1558  // Navigate to new site, simulating onbeforeunload approval.
1559  controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
1560  base::TimeTicks now = base::TimeTicks::Now();
1561  main_test_rfh()->OnMessageReceived(
1562      FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
1563  EXPECT_TRUE(contents()->cross_navigation_pending());
1564  TestRenderViewHost* rvh2 =
1565      static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
1566
1567  // Simulate rvh2's response, which leads to an unload request being sent to
1568  // rvh1.
1569  std::vector<GURL> url_chain;
1570  url_chain.push_back(GURL());
1571  contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
1572      contents()->GetRenderManagerForTesting()->pending_frame_host(),
1573      GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
1574      url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
1575  EXPECT_TRUE(contents()->cross_navigation_pending());
1576  EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK,
1577            rvh1->rvh_state());
1578
1579  // Simulate the swap out ack.
1580  rvh1->OnSwappedOut(false);
1581  EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT, rvh1->rvh_state());
1582
1583  // The new page commits.
1584  contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED);
1585  EXPECT_FALSE(contents()->cross_navigation_pending());
1586  EXPECT_EQ(rvh2, rvh());
1587  EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
1588  EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state());
1589
1590  // rvh1 should have been deleted.
1591  EXPECT_TRUE(rvh_deleted_observer.deleted());
1592  rvh1 = NULL;
1593}
1594
1595// Tests that the RenderViewHost is properly swapped out when the SwapOutACK is
1596// received before the new page commits.
1597TEST_F(RenderFrameHostManagerTest,
1598       SwapOutACKBeforeNewPageCommitsLeadsToSwapOut) {
1599  const GURL kUrl1("http://www.google.com/");
1600  const GURL kUrl2("http://www.chromium.org/");
1601
1602  // Navigate to the first page.
1603  contents()->NavigateAndCommit(kUrl1);
1604  TestRenderViewHost* rvh1 = test_rvh();
1605  RenderViewHostDeletedObserver rvh_deleted_observer(rvh1);
1606  EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
1607
1608  // Increment the number of active views in SiteInstanceImpl so that rvh2 is
1609  // not deleted on swap out.
1610  static_cast<SiteInstanceImpl*>(
1611      rvh1->GetSiteInstance())->increment_active_view_count();
1612
1613  // Navigate to new site, simulating onbeforeunload approval.
1614  controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
1615  base::TimeTicks now = base::TimeTicks::Now();
1616  main_test_rfh()->OnMessageReceived(
1617      FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
1618  EXPECT_TRUE(contents()->cross_navigation_pending());
1619  TestRenderViewHost* rvh2 =
1620      static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
1621
1622  // Simulate rvh2's response, which leads to an unload request being sent to
1623  // rvh1.
1624  std::vector<GURL> url_chain;
1625  url_chain.push_back(GURL());
1626  contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
1627      contents()->GetRenderManagerForTesting()->pending_frame_host(),
1628      GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
1629      url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
1630  EXPECT_TRUE(contents()->cross_navigation_pending());
1631  EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK,
1632            rvh1->rvh_state());
1633
1634  // Simulate the swap out ack.
1635  rvh1->OnSwappedOut(false);
1636  EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT, rvh1->rvh_state());
1637
1638  // The new page commits.
1639  contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED);
1640  EXPECT_FALSE(contents()->cross_navigation_pending());
1641  EXPECT_EQ(rvh2, rvh());
1642  EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
1643  EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state());
1644
1645  // rvh1 should be swapped out.
1646  EXPECT_FALSE(rvh_deleted_observer.deleted());
1647  EXPECT_TRUE(rvh1->IsSwappedOut());
1648}
1649
1650// Tests that the RenderViewHost is properly deleted when the new
1651// page commits before the swap out ack is received.
1652TEST_F(RenderFrameHostManagerTest,
1653       NewPageCommitsBeforeSwapOutACKLeadsToDeletion) {
1654  const GURL kUrl1("http://www.google.com/");
1655  const GURL kUrl2("http://www.chromium.org/");
1656
1657  // Navigate to the first page.
1658  contents()->NavigateAndCommit(kUrl1);
1659  TestRenderViewHost* rvh1 = test_rvh();
1660  RenderViewHostDeletedObserver rvh_deleted_observer(rvh1);
1661  EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
1662
1663  // Navigate to new site, simulating onbeforeunload approval.
1664  controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
1665  base::TimeTicks now = base::TimeTicks::Now();
1666  main_test_rfh()->OnMessageReceived(
1667      FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
1668  EXPECT_TRUE(contents()->cross_navigation_pending());
1669  TestRenderViewHost* rvh2 =
1670      static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
1671
1672  // Simulate rvh2's response, which leads to an unload request being sent to
1673  // rvh1.
1674  std::vector<GURL> url_chain;
1675  url_chain.push_back(GURL());
1676  contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
1677      contents()->GetRenderManagerForTesting()->pending_frame_host(),
1678      GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
1679      url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
1680  EXPECT_TRUE(contents()->cross_navigation_pending());
1681  EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK,
1682            rvh1->rvh_state());
1683
1684  // The new page commits.
1685  contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED);
1686  EXPECT_FALSE(contents()->cross_navigation_pending());
1687  EXPECT_EQ(rvh2, rvh());
1688  EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
1689  EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state());
1690  EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SHUTDOWN, rvh1->rvh_state());
1691
1692  // Simulate the swap out ack.
1693  rvh1->OnSwappedOut(false);
1694
1695  // rvh1 should have been deleted.
1696  EXPECT_TRUE(rvh_deleted_observer.deleted());
1697  rvh1 = NULL;
1698}
1699
1700// Tests that the RenderViewHost is properly swapped out when the new page
1701// commits before the swap out ack is received.
1702TEST_F(RenderFrameHostManagerTest,
1703       NewPageCommitsBeforeSwapOutACKLeadsToSwapOut) {
1704  const GURL kUrl1("http://www.google.com/");
1705  const GURL kUrl2("http://www.chromium.org/");
1706
1707  // Navigate to the first page.
1708  contents()->NavigateAndCommit(kUrl1);
1709  TestRenderViewHost* rvh1 = test_rvh();
1710  RenderViewHostDeletedObserver rvh_deleted_observer(rvh1);
1711  EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
1712
1713  // Increment the number of active views in SiteInstanceImpl so that rvh1 is
1714  // not deleted on swap out.
1715  static_cast<SiteInstanceImpl*>(
1716      rvh1->GetSiteInstance())->increment_active_view_count();
1717
1718  // Navigate to new site, simulating onbeforeunload approval.
1719  controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
1720  base::TimeTicks now = base::TimeTicks::Now();
1721  main_test_rfh()->OnMessageReceived(
1722      FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
1723  EXPECT_TRUE(contents()->cross_navigation_pending());
1724  TestRenderViewHost* rvh2 =
1725      static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
1726
1727  // Simulate rvh2's response, which leads to an unload request being sent to
1728  // rvh1.
1729  std::vector<GURL> url_chain;
1730  url_chain.push_back(GURL());
1731  contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
1732      contents()->GetRenderManagerForTesting()->pending_frame_host(),
1733      GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
1734      url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
1735  EXPECT_TRUE(contents()->cross_navigation_pending());
1736  EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK,
1737            rvh1->rvh_state());
1738
1739  // The new page commits.
1740  contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED);
1741  EXPECT_FALSE(contents()->cross_navigation_pending());
1742  EXPECT_EQ(rvh2, rvh());
1743  EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
1744  EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state());
1745  EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT, rvh1->rvh_state());
1746
1747  // Simulate the swap out ack.
1748  rvh1->OnSwappedOut(false);
1749
1750  // rvh1 should be swapped out.
1751  EXPECT_FALSE(rvh_deleted_observer.deleted());
1752  EXPECT_TRUE(rvh1->IsSwappedOut());
1753}
1754
1755// Test that the RenderViewHost is properly swapped out if a navigation in the
1756// new renderer commits before sending the SwapOut message to the old renderer.
1757// This simulates a cross-site navigation to a synchronously committing URL
1758// (e.g., a data URL) and ensures it works properly.
1759TEST_F(RenderFrameHostManagerTest,
1760       CommitNewNavigationBeforeSendingSwapOut) {
1761  const GURL kUrl1("http://www.google.com/");
1762  const GURL kUrl2("http://www.chromium.org/");
1763
1764  // Navigate to the first page.
1765  contents()->NavigateAndCommit(kUrl1);
1766  TestRenderViewHost* rvh1 = test_rvh();
1767  RenderViewHostDeletedObserver rvh_deleted_observer(rvh1);
1768  EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
1769
1770  // Increment the number of active views in SiteInstanceImpl so that rvh1 is
1771  // not deleted on swap out.
1772  static_cast<SiteInstanceImpl*>(
1773      rvh1->GetSiteInstance())->increment_active_view_count();
1774
1775  // Navigate to new site, simulating onbeforeunload approval.
1776  controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
1777  base::TimeTicks now = base::TimeTicks::Now();
1778  main_test_rfh()->OnMessageReceived(
1779      FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
1780  EXPECT_TRUE(contents()->cross_navigation_pending());
1781  TestRenderViewHost* rvh2 =
1782      static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
1783
1784  // The new page commits.
1785  contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED);
1786  EXPECT_FALSE(contents()->cross_navigation_pending());
1787  EXPECT_EQ(rvh2, rvh());
1788  EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
1789  EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state());
1790  EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT, rvh1->rvh_state());
1791
1792  // Simulate the swap out ack.
1793  rvh1->OnSwappedOut(false);
1794
1795  // rvh1 should be swapped out.
1796  EXPECT_FALSE(rvh_deleted_observer.deleted());
1797  EXPECT_TRUE(rvh1->IsSwappedOut());
1798}
1799
1800// Test that a RenderFrameHost is properly deleted or swapped out when a
1801// cross-site navigation is cancelled.
1802TEST_F(RenderFrameHostManagerTest,
1803       CancelPendingProperlyDeletesOrSwaps) {
1804  const GURL kUrl1("http://www.google.com/");
1805  const GURL kUrl2("http://www.chromium.org/");
1806  RenderFrameHostImpl* pending_rfh = NULL;
1807  base::TimeTicks now = base::TimeTicks::Now();
1808
1809  // Navigate to the first page.
1810  contents()->NavigateAndCommit(kUrl1);
1811  TestRenderViewHost* rvh1 = test_rvh();
1812  EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
1813
1814  // Navigate to a new site, starting a cross-site navigation.
1815  controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
1816  {
1817    pending_rfh = contents()->GetFrameTree()->root()->render_manager()
1818        ->pending_frame_host();
1819    RenderFrameHostDeletedObserver rvh_deleted_observer(pending_rfh);
1820
1821    // Cancel the navigation by simulating a declined beforeunload dialog.
1822    main_test_rfh()->OnMessageReceived(
1823        FrameHostMsg_BeforeUnload_ACK(0, false, now, now));
1824    EXPECT_FALSE(contents()->cross_navigation_pending());
1825
1826    // Since the pending RFH is the only one for the new SiteInstance, it should
1827    // be deleted.
1828    EXPECT_TRUE(rvh_deleted_observer.deleted());
1829  }
1830
1831  // Start another cross-site navigation.
1832  controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
1833  {
1834    pending_rfh = contents()->GetFrameTree()->root()->render_manager()
1835        ->pending_frame_host();
1836    RenderFrameHostDeletedObserver rvh_deleted_observer(pending_rfh);
1837
1838    // Increment the number of active views in the new SiteInstance, which will
1839    // cause the pending RFH to be swapped out instead of deleted.
1840    static_cast<SiteInstanceImpl*>(
1841        pending_rfh->GetSiteInstance())->increment_active_view_count();
1842
1843    main_test_rfh()->OnMessageReceived(
1844        FrameHostMsg_BeforeUnload_ACK(0, false, now, now));
1845    EXPECT_FALSE(contents()->cross_navigation_pending());
1846    EXPECT_FALSE(rvh_deleted_observer.deleted());
1847  }
1848}
1849
1850}  // namespace content
1851