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