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