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