• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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 <vector>
6 
7 #include "base/logging.h"
8 #include "base/utf_string_conversions.h"
9 #include "chrome/browser/prefs/pref_service.h"
10 #include "chrome/browser/prefs/pref_value_store.h"
11 #include "chrome/common/chrome_paths.h"
12 #include "chrome/common/pref_names.h"
13 #include "chrome/common/render_messages.h"
14 #include "chrome/common/url_constants.h"
15 #include "chrome/test/testing_pref_service.h"
16 #include "chrome/test/testing_profile.h"
17 #include "content/browser/browser_thread.h"
18 #include "content/browser/renderer_host/render_view_host.h"
19 #include "content/browser/renderer_host/render_widget_host_view.h"
20 #include "content/browser/renderer_host/test_render_view_host.h"
21 #include "content/browser/site_instance.h"
22 #include "content/browser/tab_contents/constrained_window.h"
23 #include "content/browser/tab_contents/interstitial_page.h"
24 #include "content/browser/tab_contents/navigation_controller.h"
25 #include "content/browser/tab_contents/navigation_entry.h"
26 #include "content/browser/tab_contents/test_tab_contents.h"
27 #include "content/common/bindings_policy.h"
28 #include "content/common/view_messages.h"
29 #include "ipc/ipc_channel.h"
30 #include "testing/gtest/include/gtest/gtest.h"
31 #include "ui/base/message_box_flags.h"
32 #include "webkit/glue/webkit_glue.h"
33 
34 using webkit_glue::PasswordForm;
35 
InitNavigateParams(ViewHostMsg_FrameNavigate_Params * params,int page_id,const GURL & url)36 static void InitNavigateParams(ViewHostMsg_FrameNavigate_Params* params,
37                                int page_id,
38                                const GURL& url) {
39   params->page_id = page_id;
40   params->url = url;
41   params->referrer = GURL();
42   params->transition = PageTransition::TYPED;
43   params->redirects = std::vector<GURL>();
44   params->should_update_history = false;
45   params->searchable_form_url = GURL();
46   params->searchable_form_encoding = std::string();
47   params->password_form = PasswordForm();
48   params->security_info = std::string();
49   params->gesture = NavigationGestureUser;
50   params->was_within_same_page = false;
51   params->is_post = false;
52   params->content_state = webkit_glue::CreateHistoryStateForURL(GURL(url));
53 }
54 
55 class TestInterstitialPage : public InterstitialPage {
56  public:
57   enum InterstitialState {
58     UNDECIDED = 0, // No decision taken yet.
59     OKED,          // Proceed was called.
60     CANCELED       // DontProceed was called.
61   };
62 
63   class Delegate {
64    public:
65     virtual void TestInterstitialPageDeleted(
66         TestInterstitialPage* interstitial) = 0;
67 
68    protected:
~Delegate()69     virtual ~Delegate() {}
70   };
71 
72   // IMPORTANT NOTE: if you pass stack allocated values for |state| and
73   // |deleted| (like all interstitial related tests do at this point), make sure
74   // to create an instance of the TestInterstitialPageStateGuard class on the
75   // stack in your test.  This will ensure that the TestInterstitialPage states
76   // are cleared when the test finishes.
77   // Not doing so will cause stack trashing if your test does not hide the
78   // interstitial, as in such a case it will be destroyed in the test TearDown
79   // method and will dereference the |deleted| local variable which by then is
80   // out of scope.
TestInterstitialPage(TabContents * tab,bool new_navigation,const GURL & url,InterstitialState * state,bool * deleted)81   TestInterstitialPage(TabContents* tab,
82                        bool new_navigation,
83                        const GURL& url,
84                        InterstitialState* state,
85                        bool* deleted)
86       : InterstitialPage(tab, new_navigation, url),
87         state_(state),
88         deleted_(deleted),
89         command_received_count_(0),
90         delegate_(NULL) {
91     *state_ = UNDECIDED;
92     *deleted_ = false;
93   }
94 
~TestInterstitialPage()95   virtual ~TestInterstitialPage() {
96     if (deleted_)
97       *deleted_ = true;
98     if (delegate_)
99       delegate_->TestInterstitialPageDeleted(this);
100   }
101 
DontProceed()102   virtual void DontProceed() {
103     if (state_)
104       *state_ = CANCELED;
105     InterstitialPage::DontProceed();
106   }
Proceed()107   virtual void Proceed() {
108     if (state_)
109       *state_ = OKED;
110     InterstitialPage::Proceed();
111   }
112 
command_received_count() const113   int command_received_count() const {
114     return command_received_count_;
115   }
116 
TestDomOperationResponse(const std::string & json_string)117   void TestDomOperationResponse(const std::string& json_string) {
118     DomOperationResponse(json_string, 1);
119   }
120 
TestDidNavigate(int page_id,const GURL & url)121   void TestDidNavigate(int page_id, const GURL& url) {
122     ViewHostMsg_FrameNavigate_Params params;
123     InitNavigateParams(&params, page_id, url);
124     DidNavigate(render_view_host(), params);
125   }
126 
TestRenderViewGone(base::TerminationStatus status,int error_code)127   void TestRenderViewGone(base::TerminationStatus status, int error_code) {
128     RenderViewGone(render_view_host(), status, error_code);
129   }
130 
is_showing() const131   bool is_showing() const {
132     return static_cast<TestRenderWidgetHostView*>(render_view_host()->view())->
133         is_showing();
134   }
135 
ClearStates()136   void ClearStates() {
137     state_ = NULL;
138     deleted_ = NULL;
139     delegate_ = NULL;
140   }
141 
set_delegate(Delegate * delegate)142   void set_delegate(Delegate* delegate) {
143     delegate_ = delegate;
144   }
145 
146  protected:
CreateRenderViewHost()147   virtual RenderViewHost* CreateRenderViewHost() {
148     return new TestRenderViewHost(
149         SiteInstance::CreateSiteInstance(tab()->profile()),
150         this, MSG_ROUTING_NONE);
151   }
152 
CreateTabContentsView()153   virtual TabContentsView* CreateTabContentsView() { return NULL; }
154 
155 
CommandReceived(const std::string & command)156   virtual void CommandReceived(const std::string& command) {
157     command_received_count_++;
158   }
159 
160  private:
161   InterstitialState* state_;
162   bool* deleted_;
163   int command_received_count_;
164   Delegate* delegate_;
165 };
166 
167 class TestInterstitialPageStateGuard : public TestInterstitialPage::Delegate {
168  public:
TestInterstitialPageStateGuard(TestInterstitialPage * interstitial_page)169   explicit TestInterstitialPageStateGuard(
170       TestInterstitialPage* interstitial_page)
171       : interstitial_page_(interstitial_page) {
172     DCHECK(interstitial_page_);
173     interstitial_page_->set_delegate(this);
174   }
~TestInterstitialPageStateGuard()175   ~TestInterstitialPageStateGuard() {
176     if (interstitial_page_)
177       interstitial_page_->ClearStates();
178   }
179 
TestInterstitialPageDeleted(TestInterstitialPage * interstitial)180   virtual void TestInterstitialPageDeleted(TestInterstitialPage* interstitial) {
181     DCHECK(interstitial_page_ == interstitial);
182     interstitial_page_ = NULL;
183   }
184 
185  private:
186   TestInterstitialPage* interstitial_page_;
187 };
188 
189 class TabContentsTest : public RenderViewHostTestHarness {
190  public:
TabContentsTest()191   TabContentsTest()
192       : RenderViewHostTestHarness(),
193         ui_thread_(BrowserThread::UI, &message_loop_) {
194   }
195 
196  private:
197   // Supply our own profile so we use the correct profile data. The test harness
198   // is not supposed to overwrite a profile if it's already created.
SetUp()199   virtual void SetUp() {
200     TestingProfile* profile = new TestingProfile();
201     profile_.reset(profile);
202 
203     // Set some (WebKit) user preferences.
204     TestingPrefService* pref_services = profile->GetTestingPrefService();
205 #if defined(TOOLKIT_USES_GTK)
206     pref_services->SetUserPref(prefs::kUsesSystemTheme,
207                                Value::CreateBooleanValue(false));
208 #endif
209     pref_services->SetUserPref(prefs::kDefaultCharset,
210                                Value::CreateStringValue("utf8"));
211     pref_services->SetUserPref(prefs::kWebKitDefaultFontSize,
212                                Value::CreateIntegerValue(20));
213     pref_services->SetUserPref(prefs::kWebKitTextAreasAreResizable,
214                                Value::CreateBooleanValue(false));
215     pref_services->SetUserPref(prefs::kWebKitUsesUniversalDetector,
216                                Value::CreateBooleanValue(true));
217     pref_services->SetUserPref("webkit.webprefs.foo",
218                                Value::CreateStringValue("bar"));
219 
220     RenderViewHostTestHarness::SetUp();
221   }
222 
TearDown()223   virtual void TearDown() {
224     RenderViewHostTestHarness::TearDown();
225 
226     profile_.reset(NULL);
227   }
228 
229   BrowserThread ui_thread_;
230 };
231 
232 // Test to make sure that title updates get stripped of whitespace.
TEST_F(TabContentsTest,UpdateTitle)233 TEST_F(TabContentsTest, UpdateTitle) {
234   ViewHostMsg_FrameNavigate_Params params;
235   InitNavigateParams(&params, 0, GURL(chrome::kAboutBlankURL));
236 
237   NavigationController::LoadCommittedDetails details;
238   controller().RendererDidNavigate(params, 0, &details);
239 
240   contents()->UpdateTitle(rvh(), 0, L"    Lots O' Whitespace\n");
241   EXPECT_EQ(ASCIIToUTF16("Lots O' Whitespace"), contents()->GetTitle());
242 }
243 
244 // Test view source mode for the new tabs page.
TEST_F(TabContentsTest,NTPViewSource)245 TEST_F(TabContentsTest, NTPViewSource) {
246   const char kUrl[] = "view-source:chrome://newtab";
247   const GURL kGURL(kUrl);
248 
249   process()->sink().ClearMessages();
250 
251   controller().LoadURL(kGURL, GURL(), PageTransition::TYPED);
252   rvh()->delegate()->RenderViewCreated(rvh());
253   // Did we get the expected message?
254   EXPECT_TRUE(process()->sink().GetFirstMessageMatching(
255       ViewMsg_EnableViewSourceMode::ID));
256 
257   ViewHostMsg_FrameNavigate_Params params;
258   InitNavigateParams(&params, 0, kGURL);
259   NavigationController::LoadCommittedDetails details;
260   controller().RendererDidNavigate(params, 0, &details);
261   // Also check title and url.
262   EXPECT_EQ(ASCIIToUTF16(kUrl), contents()->GetTitle());
263   EXPECT_TRUE(contents()->ShouldDisplayURL());
264 }
265 
266 // Test simple same-SiteInstance navigation.
TEST_F(TabContentsTest,SimpleNavigation)267 TEST_F(TabContentsTest, SimpleNavigation) {
268   TestRenderViewHost* orig_rvh = rvh();
269   SiteInstance* instance1 = contents()->GetSiteInstance();
270   EXPECT_TRUE(contents()->pending_rvh() == NULL);
271 
272   // Navigate to URL
273   const GURL url("http://www.google.com");
274   controller().LoadURL(url, GURL(), PageTransition::TYPED);
275   EXPECT_FALSE(contents()->cross_navigation_pending());
276   EXPECT_EQ(instance1, orig_rvh->site_instance());
277   // Controller's pending entry will have a NULL site instance until we assign
278   // it in DidNavigate.
279   EXPECT_TRUE(controller().GetActiveEntry()->site_instance() == NULL);
280 
281   // DidNavigate from the page
282   ViewHostMsg_FrameNavigate_Params params;
283   InitNavigateParams(&params, 1, url);
284   contents()->TestDidNavigate(orig_rvh, params);
285   EXPECT_FALSE(contents()->cross_navigation_pending());
286   EXPECT_EQ(orig_rvh, contents()->render_view_host());
287   EXPECT_EQ(instance1, orig_rvh->site_instance());
288   // Controller's entry should now have the SiteInstance, or else we won't be
289   // able to find it later.
290   EXPECT_EQ(instance1, controller().GetActiveEntry()->site_instance());
291 }
292 
293 // Test that navigating across a site boundary creates a new RenderViewHost
294 // with a new SiteInstance.  Going back should do the same.
TEST_F(TabContentsTest,CrossSiteBoundaries)295 TEST_F(TabContentsTest, CrossSiteBoundaries) {
296   contents()->transition_cross_site = true;
297   TestRenderViewHost* orig_rvh = rvh();
298   int orig_rvh_delete_count = 0;
299   orig_rvh->set_delete_counter(&orig_rvh_delete_count);
300   SiteInstance* instance1 = contents()->GetSiteInstance();
301 
302   // Navigate to URL.  First URL should use first RenderViewHost.
303   const GURL url("http://www.google.com");
304   controller().LoadURL(url, GURL(), PageTransition::TYPED);
305   ViewHostMsg_FrameNavigate_Params params1;
306   InitNavigateParams(&params1, 1, url);
307   contents()->TestDidNavigate(orig_rvh, params1);
308 
309   EXPECT_FALSE(contents()->cross_navigation_pending());
310   EXPECT_EQ(orig_rvh, contents()->render_view_host());
311 
312   // Navigate to new site
313   const GURL url2("http://www.yahoo.com");
314   controller().LoadURL(url2, GURL(), PageTransition::TYPED);
315   EXPECT_TRUE(contents()->cross_navigation_pending());
316   TestRenderViewHost* pending_rvh = contents()->pending_rvh();
317   int pending_rvh_delete_count = 0;
318   pending_rvh->set_delete_counter(&pending_rvh_delete_count);
319 
320   // DidNavigate from the pending page
321   ViewHostMsg_FrameNavigate_Params params2;
322   InitNavigateParams(&params2, 1, url2);
323   contents()->TestDidNavigate(pending_rvh, params2);
324   SiteInstance* instance2 = contents()->GetSiteInstance();
325 
326   EXPECT_FALSE(contents()->cross_navigation_pending());
327   EXPECT_EQ(pending_rvh, contents()->render_view_host());
328   EXPECT_NE(instance1, instance2);
329   EXPECT_TRUE(contents()->pending_rvh() == NULL);
330   EXPECT_EQ(orig_rvh_delete_count, 1);
331 
332   // Going back should switch SiteInstances again.  The first SiteInstance is
333   // stored in the NavigationEntry, so it should be the same as at the start.
334   controller().GoBack();
335   TestRenderViewHost* goback_rvh = contents()->pending_rvh();
336   EXPECT_TRUE(contents()->cross_navigation_pending());
337 
338   // DidNavigate from the back action
339   contents()->TestDidNavigate(goback_rvh, params1);
340   EXPECT_FALSE(contents()->cross_navigation_pending());
341   EXPECT_EQ(goback_rvh, contents()->render_view_host());
342   EXPECT_EQ(pending_rvh_delete_count, 1);
343   EXPECT_EQ(instance1, contents()->GetSiteInstance());
344 }
345 
346 // Test that navigating across a site boundary after a crash creates a new
347 // RVH without requiring a cross-site transition (i.e., PENDING state).
TEST_F(TabContentsTest,CrossSiteBoundariesAfterCrash)348 TEST_F(TabContentsTest, CrossSiteBoundariesAfterCrash) {
349   contents()->transition_cross_site = true;
350   TestRenderViewHost* orig_rvh = rvh();
351   int orig_rvh_delete_count = 0;
352   orig_rvh->set_delete_counter(&orig_rvh_delete_count);
353   SiteInstance* instance1 = contents()->GetSiteInstance();
354 
355   // Navigate to URL.  First URL should use first RenderViewHost.
356   const GURL url("http://www.google.com");
357   controller().LoadURL(url, GURL(), PageTransition::TYPED);
358   ViewHostMsg_FrameNavigate_Params params1;
359   InitNavigateParams(&params1, 1, url);
360   contents()->TestDidNavigate(orig_rvh, params1);
361 
362   EXPECT_FALSE(contents()->cross_navigation_pending());
363   EXPECT_EQ(orig_rvh, contents()->render_view_host());
364 
365   // Crash the renderer.
366   orig_rvh->set_render_view_created(false);
367 
368   // Navigate to new site.  We should not go into PENDING.
369   const GURL url2("http://www.yahoo.com");
370   controller().LoadURL(url2, GURL(), PageTransition::TYPED);
371   TestRenderViewHost* new_rvh = rvh();
372   EXPECT_FALSE(contents()->cross_navigation_pending());
373   EXPECT_TRUE(contents()->pending_rvh() == NULL);
374   EXPECT_NE(orig_rvh, new_rvh);
375   EXPECT_EQ(orig_rvh_delete_count, 1);
376 
377   // DidNavigate from the new page
378   ViewHostMsg_FrameNavigate_Params params2;
379   InitNavigateParams(&params2, 1, url2);
380   contents()->TestDidNavigate(new_rvh, params2);
381   SiteInstance* instance2 = contents()->GetSiteInstance();
382 
383   EXPECT_FALSE(contents()->cross_navigation_pending());
384   EXPECT_EQ(new_rvh, rvh());
385   EXPECT_NE(instance1, instance2);
386   EXPECT_TRUE(contents()->pending_rvh() == NULL);
387 }
388 
389 // Test that opening a new tab in the same SiteInstance and then navigating
390 // both tabs to a new site will place both tabs in a single SiteInstance.
TEST_F(TabContentsTest,NavigateTwoTabsCrossSite)391 TEST_F(TabContentsTest, NavigateTwoTabsCrossSite) {
392   contents()->transition_cross_site = true;
393   TestRenderViewHost* orig_rvh = rvh();
394   SiteInstance* instance1 = contents()->GetSiteInstance();
395 
396   // Navigate to URL.  First URL should use first RenderViewHost.
397   const GURL url("http://www.google.com");
398   controller().LoadURL(url, GURL(), PageTransition::TYPED);
399   ViewHostMsg_FrameNavigate_Params params1;
400   InitNavigateParams(&params1, 1, url);
401   contents()->TestDidNavigate(orig_rvh, params1);
402 
403   // Open a new tab with the same SiteInstance, navigated to the same site.
404   TestTabContents contents2(profile(), instance1);
405   params1.page_id = 2;  // Need this since the site instance is the same (which
406                         // is the scope of page IDs) and we want to consider
407                         // this a new page.
408   contents2.transition_cross_site = true;
409   contents2.controller().LoadURL(url, GURL(), PageTransition::TYPED);
410   contents2.TestDidNavigate(contents2.render_view_host(), params1);
411 
412   // Navigate first tab to a new site
413   const GURL url2a("http://www.yahoo.com");
414   controller().LoadURL(url2a, GURL(), PageTransition::TYPED);
415   TestRenderViewHost* pending_rvh_a = contents()->pending_rvh();
416   ViewHostMsg_FrameNavigate_Params params2a;
417   InitNavigateParams(&params2a, 1, url2a);
418   contents()->TestDidNavigate(pending_rvh_a, params2a);
419   SiteInstance* instance2a = contents()->GetSiteInstance();
420   EXPECT_NE(instance1, instance2a);
421 
422   // Navigate second tab to the same site as the first tab
423   const GURL url2b("http://mail.yahoo.com");
424   contents2.controller().LoadURL(url2b, GURL(), PageTransition::TYPED);
425   TestRenderViewHost* pending_rvh_b = contents2.pending_rvh();
426   EXPECT_TRUE(pending_rvh_b != NULL);
427   EXPECT_TRUE(contents2.cross_navigation_pending());
428 
429   // NOTE(creis): We used to be in danger of showing a sad tab page here if the
430   // second tab hadn't navigated somewhere first (bug 1145430).  That case is
431   // now covered by the CrossSiteBoundariesAfterCrash test.
432 
433   ViewHostMsg_FrameNavigate_Params params2b;
434   InitNavigateParams(&params2b, 2, url2b);
435   contents2.TestDidNavigate(pending_rvh_b, params2b);
436   SiteInstance* instance2b = contents2.GetSiteInstance();
437   EXPECT_NE(instance1, instance2b);
438 
439   // Both tabs should now be in the same SiteInstance.
440   EXPECT_EQ(instance2a, instance2b);
441 }
442 
443 // Tests that TabContents uses the current URL, not the SiteInstance's site, to
444 // determine whether a navigation is cross-site.
TEST_F(TabContentsTest,CrossSiteComparesAgainstCurrentPage)445 TEST_F(TabContentsTest, CrossSiteComparesAgainstCurrentPage) {
446   contents()->transition_cross_site = true;
447   TestRenderViewHost* orig_rvh = rvh();
448   SiteInstance* instance1 = contents()->GetSiteInstance();
449 
450   // Navigate to URL.
451   const GURL url("http://www.google.com");
452   controller().LoadURL(url, GURL(), PageTransition::TYPED);
453   ViewHostMsg_FrameNavigate_Params params1;
454   InitNavigateParams(&params1, 1, url);
455   contents()->TestDidNavigate(orig_rvh, params1);
456 
457   // Open a related tab to a second site.
458   TestTabContents contents2(profile(), instance1);
459   contents2.transition_cross_site = true;
460   const GURL url2("http://www.yahoo.com");
461   contents2.controller().LoadURL(url2, GURL(), PageTransition::TYPED);
462   // The first RVH in contents2 isn't live yet, so we shortcut the cross site
463   // pending.
464   TestRenderViewHost* rvh2 = static_cast<TestRenderViewHost*>(
465       contents2.render_view_host());
466   EXPECT_FALSE(contents2.cross_navigation_pending());
467   ViewHostMsg_FrameNavigate_Params params2;
468   InitNavigateParams(&params2, 2, url2);
469   contents2.TestDidNavigate(rvh2, params2);
470   SiteInstance* instance2 = contents2.GetSiteInstance();
471   EXPECT_NE(instance1, instance2);
472   EXPECT_FALSE(contents2.cross_navigation_pending());
473 
474   // Simulate a link click in first tab to second site.  Doesn't switch
475   // SiteInstances, because we don't intercept WebKit navigations.
476   ViewHostMsg_FrameNavigate_Params params3;
477   InitNavigateParams(&params3, 2, url2);
478   contents()->TestDidNavigate(orig_rvh, params3);
479   SiteInstance* instance3 = contents()->GetSiteInstance();
480   EXPECT_EQ(instance1, instance3);
481   EXPECT_FALSE(contents()->cross_navigation_pending());
482 
483   // Navigate to the new site.  Doesn't switch SiteInstancees, because we
484   // compare against the current URL, not the SiteInstance's site.
485   const GURL url3("http://mail.yahoo.com");
486   controller().LoadURL(url3, GURL(), PageTransition::TYPED);
487   EXPECT_FALSE(contents()->cross_navigation_pending());
488   ViewHostMsg_FrameNavigate_Params params4;
489   InitNavigateParams(&params4, 3, url3);
490   contents()->TestDidNavigate(orig_rvh, params4);
491   SiteInstance* instance4 = contents()->GetSiteInstance();
492   EXPECT_EQ(instance1, instance4);
493 }
494 
495 // Test that the onbeforeunload and onunload handlers run when navigating
496 // across site boundaries.
TEST_F(TabContentsTest,CrossSiteUnloadHandlers)497 TEST_F(TabContentsTest, CrossSiteUnloadHandlers) {
498   contents()->transition_cross_site = true;
499   TestRenderViewHost* orig_rvh = rvh();
500   SiteInstance* instance1 = contents()->GetSiteInstance();
501 
502   // Navigate to URL.  First URL should use first RenderViewHost.
503   const GURL url("http://www.google.com");
504   controller().LoadURL(url, GURL(), PageTransition::TYPED);
505   ViewHostMsg_FrameNavigate_Params params1;
506   InitNavigateParams(&params1, 1, url);
507   contents()->TestDidNavigate(orig_rvh, params1);
508   EXPECT_FALSE(contents()->cross_navigation_pending());
509   EXPECT_EQ(orig_rvh, contents()->render_view_host());
510 
511   // Navigate to new site, but simulate an onbeforeunload denial.
512   const GURL url2("http://www.yahoo.com");
513   controller().LoadURL(url2, GURL(), PageTransition::TYPED);
514   EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
515   orig_rvh->TestOnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, false));
516   EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
517   EXPECT_FALSE(contents()->cross_navigation_pending());
518   EXPECT_EQ(orig_rvh, contents()->render_view_host());
519 
520   // Navigate again, but simulate an onbeforeunload approval.
521   controller().LoadURL(url2, GURL(), PageTransition::TYPED);
522   EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
523   orig_rvh->TestOnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true));
524   EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
525   EXPECT_TRUE(contents()->cross_navigation_pending());
526   TestRenderViewHost* pending_rvh = static_cast<TestRenderViewHost*>(
527       contents()->pending_rvh());
528 
529   // We won't hear DidNavigate until the onunload handler has finished running.
530   // (No way to simulate that here, but it involves a call from RDH to
531   // TabContents::OnCrossSiteResponse.)
532 
533   // DidNavigate from the pending page
534   ViewHostMsg_FrameNavigate_Params params2;
535   InitNavigateParams(&params2, 1, url2);
536   contents()->TestDidNavigate(pending_rvh, params2);
537   SiteInstance* instance2 = contents()->GetSiteInstance();
538   EXPECT_FALSE(contents()->cross_navigation_pending());
539   EXPECT_EQ(pending_rvh, rvh());
540   EXPECT_NE(instance1, instance2);
541   EXPECT_TRUE(contents()->pending_rvh() == NULL);
542 }
543 
544 // Test that during a slow cross-site navigation, the original renderer can
545 // navigate to a different URL and have it displayed, canceling the slow
546 // navigation.
TEST_F(TabContentsTest,CrossSiteNavigationPreempted)547 TEST_F(TabContentsTest, CrossSiteNavigationPreempted) {
548   contents()->transition_cross_site = true;
549   TestRenderViewHost* orig_rvh = rvh();
550   SiteInstance* instance1 = contents()->GetSiteInstance();
551 
552   // Navigate to URL.  First URL should use first RenderViewHost.
553   const GURL url("http://www.google.com");
554   controller().LoadURL(url, GURL(), PageTransition::TYPED);
555   ViewHostMsg_FrameNavigate_Params params1;
556   InitNavigateParams(&params1, 1, url);
557   contents()->TestDidNavigate(orig_rvh, params1);
558   EXPECT_FALSE(contents()->cross_navigation_pending());
559   EXPECT_EQ(orig_rvh, contents()->render_view_host());
560 
561   // Navigate to new site, simulating an onbeforeunload approval.
562   const GURL url2("http://www.yahoo.com");
563   controller().LoadURL(url2, GURL(), PageTransition::TYPED);
564   EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
565   orig_rvh->TestOnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true));
566   EXPECT_TRUE(contents()->cross_navigation_pending());
567 
568   // Suppose the original renderer navigates before the new one is ready.
569   orig_rvh->SendNavigate(2, GURL("http://www.google.com/foo"));
570 
571   // Verify that the pending navigation is cancelled.
572   EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
573   SiteInstance* instance2 = contents()->GetSiteInstance();
574   EXPECT_FALSE(contents()->cross_navigation_pending());
575   EXPECT_EQ(orig_rvh, rvh());
576   EXPECT_EQ(instance1, instance2);
577   EXPECT_TRUE(contents()->pending_rvh() == NULL);
578 }
579 
TEST_F(TabContentsTest,CrossSiteNavigationBackPreempted)580 TEST_F(TabContentsTest, CrossSiteNavigationBackPreempted) {
581   contents()->transition_cross_site = true;
582 
583   // Start with NTP, which gets a new RVH with WebUI bindings.
584   const GURL url1("chrome://newtab");
585   controller().LoadURL(url1, GURL(), PageTransition::TYPED);
586   TestRenderViewHost* ntp_rvh = rvh();
587   ViewHostMsg_FrameNavigate_Params params1;
588   InitNavigateParams(&params1, 1, url1);
589   contents()->TestDidNavigate(ntp_rvh, params1);
590   NavigationEntry* entry1 = controller().GetLastCommittedEntry();
591   SiteInstance* instance1 = contents()->GetSiteInstance();
592 
593   EXPECT_FALSE(contents()->cross_navigation_pending());
594   EXPECT_EQ(ntp_rvh, contents()->render_view_host());
595   EXPECT_EQ(url1, entry1->url());
596   EXPECT_EQ(instance1, entry1->site_instance());
597   EXPECT_TRUE(BindingsPolicy::is_web_ui_enabled(ntp_rvh->enabled_bindings()));
598 
599   // Navigate to new site.
600   const GURL url2("http://www.google.com");
601   controller().LoadURL(url2, GURL(), PageTransition::TYPED);
602   EXPECT_TRUE(contents()->cross_navigation_pending());
603   TestRenderViewHost* google_rvh = contents()->pending_rvh();
604 
605   // Simulate beforeunload approval.
606   EXPECT_TRUE(ntp_rvh->is_waiting_for_beforeunload_ack());
607   ntp_rvh->TestOnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true));
608 
609   // DidNavigate from the pending page.
610   ViewHostMsg_FrameNavigate_Params params2;
611   InitNavigateParams(&params2, 1, url2);
612   contents()->TestDidNavigate(google_rvh, params2);
613   NavigationEntry* entry2 = controller().GetLastCommittedEntry();
614   SiteInstance* instance2 = contents()->GetSiteInstance();
615 
616   EXPECT_FALSE(contents()->cross_navigation_pending());
617   EXPECT_EQ(google_rvh, contents()->render_view_host());
618   EXPECT_NE(instance1, instance2);
619   EXPECT_FALSE(contents()->pending_rvh());
620   EXPECT_EQ(url2, entry2->url());
621   EXPECT_EQ(instance2, entry2->site_instance());
622   EXPECT_FALSE(BindingsPolicy::is_web_ui_enabled(
623       google_rvh->enabled_bindings()));
624 
625   // Navigate to third page on same site.
626   const GURL url3("http://news.google.com");
627   controller().LoadURL(url3, GURL(), PageTransition::TYPED);
628   EXPECT_FALSE(contents()->cross_navigation_pending());
629   ViewHostMsg_FrameNavigate_Params params3;
630   InitNavigateParams(&params3, 2, url3);
631   contents()->TestDidNavigate(google_rvh, params3);
632   NavigationEntry* entry3 = controller().GetLastCommittedEntry();
633   SiteInstance* instance3 = contents()->GetSiteInstance();
634 
635   EXPECT_FALSE(contents()->cross_navigation_pending());
636   EXPECT_EQ(google_rvh, contents()->render_view_host());
637   EXPECT_EQ(instance2, instance3);
638   EXPECT_FALSE(contents()->pending_rvh());
639   EXPECT_EQ(url3, entry3->url());
640   EXPECT_EQ(instance3, entry3->site_instance());
641 
642   // Go back within the site.
643   controller().GoBack();
644   EXPECT_FALSE(contents()->cross_navigation_pending());
645   EXPECT_EQ(entry2, controller().pending_entry());
646 
647   // Before that commits, go back again.
648   controller().GoBack();
649   EXPECT_TRUE(contents()->cross_navigation_pending());
650   EXPECT_TRUE(contents()->pending_rvh());
651   EXPECT_EQ(entry1, controller().pending_entry());
652 
653   // Simulate beforeunload approval.
654   EXPECT_TRUE(google_rvh->is_waiting_for_beforeunload_ack());
655   google_rvh->TestOnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true));
656 
657   // DidNavigate from the first back. This aborts the second back's pending RVH.
658   contents()->TestDidNavigate(google_rvh, params2);
659 
660   // We should commit this page and forget about the second back.
661   EXPECT_FALSE(contents()->cross_navigation_pending());
662   EXPECT_FALSE(controller().pending_entry());
663   EXPECT_EQ(google_rvh, contents()->render_view_host());
664   EXPECT_EQ(url2, controller().GetLastCommittedEntry()->url());
665 
666   // We should not have corrupted the NTP entry.
667   EXPECT_EQ(instance3, entry3->site_instance());
668   EXPECT_EQ(instance2, entry2->site_instance());
669   EXPECT_EQ(instance1, entry1->site_instance());
670   EXPECT_EQ(url1, entry1->url());
671 }
672 
673 // Test that during a slow cross-site navigation, a sub-frame navigation in the
674 // original renderer will not cancel the slow navigation (bug 42029).
TEST_F(TabContentsTest,CrossSiteNavigationNotPreemptedByFrame)675 TEST_F(TabContentsTest, CrossSiteNavigationNotPreemptedByFrame) {
676   contents()->transition_cross_site = true;
677   TestRenderViewHost* orig_rvh = rvh();
678 
679   // Navigate to URL.  First URL should use first RenderViewHost.
680   const GURL url("http://www.google.com");
681   controller().LoadURL(url, GURL(), PageTransition::TYPED);
682   ViewHostMsg_FrameNavigate_Params params1;
683   InitNavigateParams(&params1, 1, url);
684   contents()->TestDidNavigate(orig_rvh, params1);
685   EXPECT_FALSE(contents()->cross_navigation_pending());
686   EXPECT_EQ(orig_rvh, contents()->render_view_host());
687 
688   // Start navigating to new site.
689   const GURL url2("http://www.yahoo.com");
690   controller().LoadURL(url2, GURL(), PageTransition::TYPED);
691 
692   // Simulate a sub-frame navigation arriving and ensure the RVH is still
693   // waiting for a before unload response.
694   orig_rvh->SendNavigateWithTransition(1, GURL("http://google.com/frame"),
695                                        PageTransition::AUTO_SUBFRAME);
696   EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
697 
698   // Now simulate the onbeforeunload approval and verify the navigation is
699   // not canceled.
700   orig_rvh->TestOnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true));
701   EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
702   EXPECT_TRUE(contents()->cross_navigation_pending());
703 }
704 
705 // Test that a cross-site navigation is not preempted if the previous
706 // renderer sends a FrameNavigate message just before being told to stop.
707 // We should only preempt the cross-site navigation if the previous renderer
708 // has started a new navigation.  See http://crbug.com/79176.
TEST_F(TabContentsTest,CrossSiteNotPreemptedDuringBeforeUnload)709 TEST_F(TabContentsTest, CrossSiteNotPreemptedDuringBeforeUnload) {
710   contents()->transition_cross_site = true;
711 
712   // Navigate to NTP URL.
713   const GURL url("chrome://newtab");
714   controller().LoadURL(url, GURL(), PageTransition::TYPED);
715   TestRenderViewHost* orig_rvh = rvh();
716   EXPECT_FALSE(contents()->cross_navigation_pending());
717 
718   // Navigate to new site, with the beforeunload request in flight.
719   const GURL url2("http://www.yahoo.com");
720   controller().LoadURL(url2, GURL(), PageTransition::TYPED);
721   TestRenderViewHost* pending_rvh = contents()->pending_rvh();
722   EXPECT_TRUE(contents()->cross_navigation_pending());
723   EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
724 
725   // Suppose the first navigation tries to commit now, with a
726   // ViewMsg_Stop in flight.  This should not cancel the pending navigation,
727   // but it should act as if the beforeunload ack arrived.
728   orig_rvh->SendNavigate(1, GURL("chrome://newtab"));
729   EXPECT_TRUE(contents()->cross_navigation_pending());
730   EXPECT_EQ(orig_rvh, contents()->render_view_host());
731   EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
732 
733   // The pending navigation should be able to commit successfully.
734   ViewHostMsg_FrameNavigate_Params params2;
735   InitNavigateParams(&params2, 1, url2, PageTransition::TYPED);
736   contents()->TestDidNavigate(pending_rvh, params2);
737   EXPECT_FALSE(contents()->cross_navigation_pending());
738   EXPECT_EQ(pending_rvh, contents()->render_view_host());
739 }
740 
741 // Test that the original renderer cannot preempt a cross-site navigation once
742 // the unload request has been made.  At this point, the cross-site navigation
743 // is almost ready to be displayed, and the original renderer is only given a
744 // short chance to run an unload handler.  Prevents regression of bug 23942.
TEST_F(TabContentsTest,CrossSiteCantPreemptAfterUnload)745 TEST_F(TabContentsTest, CrossSiteCantPreemptAfterUnload) {
746   contents()->transition_cross_site = true;
747   TestRenderViewHost* orig_rvh = rvh();
748   SiteInstance* instance1 = contents()->GetSiteInstance();
749 
750   // Navigate to URL.  First URL should use first RenderViewHost.
751   const GURL url("http://www.google.com");
752   controller().LoadURL(url, GURL(), PageTransition::TYPED);
753   ViewHostMsg_FrameNavigate_Params params1;
754   InitNavigateParams(&params1, 1, url);
755   contents()->TestDidNavigate(orig_rvh, params1);
756   EXPECT_FALSE(contents()->cross_navigation_pending());
757   EXPECT_EQ(orig_rvh, contents()->render_view_host());
758 
759   // Navigate to new site, simulating an onbeforeunload approval.
760   const GURL url2("http://www.yahoo.com");
761   controller().LoadURL(url2, GURL(), PageTransition::TYPED);
762   orig_rvh->TestOnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true));
763   EXPECT_TRUE(contents()->cross_navigation_pending());
764   TestRenderViewHost* pending_rvh = static_cast<TestRenderViewHost*>(
765       contents()->pending_rvh());
766 
767   // Simulate the pending renderer's response, which leads to an unload request
768   // being sent to orig_rvh.
769   contents()->OnCrossSiteResponse(0, 0);
770 
771   // Suppose the original renderer navigates now, while the unload request is in
772   // flight.  We should ignore it, wait for the unload ack, and let the pending
773   // request continue.  Otherwise, the tab may close spontaneously or stop
774   // responding to navigation requests.  (See bug 23942.)
775   ViewHostMsg_FrameNavigate_Params params1a;
776   InitNavigateParams(&params1a, 2, GURL("http://www.google.com/foo"));
777   orig_rvh->SendNavigate(2, GURL("http://www.google.com/foo"));
778 
779   // Verify that the pending navigation is still in progress.
780   EXPECT_TRUE(contents()->cross_navigation_pending());
781   EXPECT_TRUE(contents()->pending_rvh() != NULL);
782 
783   // DidNavigate from the pending page should commit it.
784   ViewHostMsg_FrameNavigate_Params params2;
785   InitNavigateParams(&params2, 1, url2);
786   contents()->TestDidNavigate(pending_rvh, params2);
787   SiteInstance* instance2 = contents()->GetSiteInstance();
788   EXPECT_FALSE(contents()->cross_navigation_pending());
789   EXPECT_EQ(pending_rvh, rvh());
790   EXPECT_NE(instance1, instance2);
791   EXPECT_TRUE(contents()->pending_rvh() == NULL);
792 }
793 
794 // Test that NavigationEntries have the correct content state after going
795 // forward and back.  Prevents regression for bug 1116137.
TEST_F(TabContentsTest,NavigationEntryContentState)796 TEST_F(TabContentsTest, NavigationEntryContentState) {
797   TestRenderViewHost* orig_rvh = rvh();
798 
799   // Navigate to URL.  There should be no committed entry yet.
800   const GURL url("http://www.google.com");
801   controller().LoadURL(url, GURL(), PageTransition::TYPED);
802   NavigationEntry* entry = controller().GetLastCommittedEntry();
803   EXPECT_TRUE(entry == NULL);
804 
805   // Committed entry should have content state after DidNavigate.
806   ViewHostMsg_FrameNavigate_Params params1;
807   InitNavigateParams(&params1, 1, url);
808   contents()->TestDidNavigate(orig_rvh, params1);
809   entry = controller().GetLastCommittedEntry();
810   EXPECT_FALSE(entry->content_state().empty());
811 
812   // Navigate to same site.
813   const GURL url2("http://images.google.com");
814   controller().LoadURL(url2, GURL(), PageTransition::TYPED);
815   entry = controller().GetLastCommittedEntry();
816   EXPECT_FALSE(entry->content_state().empty());
817 
818   // Committed entry should have content state after DidNavigate.
819   ViewHostMsg_FrameNavigate_Params params2;
820   InitNavigateParams(&params2, 2, url2);
821   contents()->TestDidNavigate(orig_rvh, params2);
822   entry = controller().GetLastCommittedEntry();
823   EXPECT_FALSE(entry->content_state().empty());
824 
825   // Now go back.  Committed entry should still have content state.
826   controller().GoBack();
827   contents()->TestDidNavigate(orig_rvh, params1);
828   entry = controller().GetLastCommittedEntry();
829   EXPECT_FALSE(entry->content_state().empty());
830 }
831 
832 // Test that NavigationEntries have the correct content state after opening
833 // a new window to about:blank.  Prevents regression for bug 1116137.
TEST_F(TabContentsTest,NavigationEntryContentStateNewWindow)834 TEST_F(TabContentsTest, NavigationEntryContentStateNewWindow) {
835   TestRenderViewHost* orig_rvh = rvh();
836 
837   // When opening a new window, it is navigated to about:blank internally.
838   // Currently, this results in two DidNavigate events.
839   const GURL url(chrome::kAboutBlankURL);
840   ViewHostMsg_FrameNavigate_Params params1;
841   InitNavigateParams(&params1, 1, url);
842   contents()->TestDidNavigate(orig_rvh, params1);
843   contents()->TestDidNavigate(orig_rvh, params1);
844 
845   // Should have a content state here.
846   NavigationEntry* entry = controller().GetLastCommittedEntry();
847   EXPECT_FALSE(entry->content_state().empty());
848 }
849 
850 // Tests to see that webkit preferences are properly loaded and copied over
851 // to a WebPreferences object.
TEST_F(TabContentsTest,WebKitPrefs)852 TEST_F(TabContentsTest, WebKitPrefs) {
853   WebPreferences webkit_prefs = contents()->TestGetWebkitPrefs();
854 
855   // These values have been overridden by the profile preferences.
856   EXPECT_EQ("UTF-8", webkit_prefs.default_encoding);
857   EXPECT_EQ(20, webkit_prefs.default_font_size);
858   EXPECT_FALSE(webkit_prefs.text_areas_are_resizable);
859   EXPECT_TRUE(webkit_prefs.uses_universal_detector);
860 
861   // These should still be the default values.
862 #if defined(OS_MACOSX)
863   const char kDefaultFont[] = "Times";
864 #elif defined(OS_CHROMEOS)
865   const char kDefaultFont[] = "Tinos";
866 #else
867   const char kDefaultFont[] = "Times New Roman";
868 #endif
869   EXPECT_EQ(ASCIIToUTF16(kDefaultFont), webkit_prefs.standard_font_family);
870   EXPECT_TRUE(webkit_prefs.javascript_enabled);
871 }
872 
873 ////////////////////////////////////////////////////////////////////////////////
874 // Interstitial Tests
875 ////////////////////////////////////////////////////////////////////////////////
876 
877 // Test navigating to a page (with the navigation initiated from the browser,
878 // as when a URL is typed in the location bar) that shows an interstitial and
879 // creates a new navigation entry, then hiding it without proceeding.
TEST_F(TabContentsTest,ShowInterstitialFromBrowserWithNewNavigationDontProceed)880 TEST_F(TabContentsTest,
881        ShowInterstitialFromBrowserWithNewNavigationDontProceed) {
882   // Navigate to a page.
883   GURL url1("http://www.google.com");
884   rvh()->SendNavigate(1, url1);
885   EXPECT_EQ(1, controller().entry_count());
886 
887   // Initiate a browser navigation that will trigger the interstitial
888   controller().LoadURL(GURL("http://www.evil.com"), GURL(),
889                         PageTransition::TYPED);
890 
891   // Show an interstitial.
892   TestInterstitialPage::InterstitialState state =
893       TestInterstitialPage::UNDECIDED;
894   bool deleted = false;
895   GURL url2("http://interstitial");
896   TestInterstitialPage* interstitial =
897       new TestInterstitialPage(contents(), true, url2, &state, &deleted);
898   TestInterstitialPageStateGuard state_guard(interstitial);
899   interstitial->Show();
900   // The interstitial should not show until its navigation has committed.
901   EXPECT_FALSE(interstitial->is_showing());
902   EXPECT_FALSE(contents()->showing_interstitial_page());
903   EXPECT_TRUE(contents()->interstitial_page() == NULL);
904   // Let's commit the interstitial navigation.
905   interstitial->TestDidNavigate(1, url2);
906   EXPECT_TRUE(interstitial->is_showing());
907   EXPECT_TRUE(contents()->showing_interstitial_page());
908   EXPECT_TRUE(contents()->interstitial_page() == interstitial);
909   NavigationEntry* entry = controller().GetActiveEntry();
910   ASSERT_TRUE(entry != NULL);
911   EXPECT_TRUE(entry->url() == url2);
912 
913   // Now don't proceed.
914   interstitial->DontProceed();
915   EXPECT_TRUE(deleted);
916   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
917   EXPECT_FALSE(contents()->showing_interstitial_page());
918   EXPECT_TRUE(contents()->interstitial_page() == NULL);
919   entry = controller().GetActiveEntry();
920   ASSERT_TRUE(entry != NULL);
921   EXPECT_TRUE(entry->url() == url1);
922   EXPECT_EQ(1, controller().entry_count());
923 }
924 
925 // Test navigating to a page (with the navigation initiated from the renderer,
926 // as when clicking on a link in the page) that shows an interstitial and
927 // creates a new navigation entry, then hiding it without proceeding.
TEST_F(TabContentsTest,ShowInterstitiaFromRendererlWithNewNavigationDontProceed)928 TEST_F(TabContentsTest,
929        ShowInterstitiaFromRendererlWithNewNavigationDontProceed) {
930   // Navigate to a page.
931   GURL url1("http://www.google.com");
932   rvh()->SendNavigate(1, url1);
933   EXPECT_EQ(1, controller().entry_count());
934 
935   // Show an interstitial (no pending entry, the interstitial would have been
936   // triggered by clicking on a link).
937   TestInterstitialPage::InterstitialState state =
938       TestInterstitialPage::UNDECIDED;
939   bool deleted = false;
940   GURL url2("http://interstitial");
941   TestInterstitialPage* interstitial =
942       new TestInterstitialPage(contents(), true, url2, &state, &deleted);
943   TestInterstitialPageStateGuard state_guard(interstitial);
944   interstitial->Show();
945   // The interstitial should not show until its navigation has committed.
946   EXPECT_FALSE(interstitial->is_showing());
947   EXPECT_FALSE(contents()->showing_interstitial_page());
948   EXPECT_TRUE(contents()->interstitial_page() == NULL);
949   // Let's commit the interstitial navigation.
950   interstitial->TestDidNavigate(1, url2);
951   EXPECT_TRUE(interstitial->is_showing());
952   EXPECT_TRUE(contents()->showing_interstitial_page());
953   EXPECT_TRUE(contents()->interstitial_page() == interstitial);
954   NavigationEntry* entry = controller().GetActiveEntry();
955   ASSERT_TRUE(entry != NULL);
956   EXPECT_TRUE(entry->url() == url2);
957 
958   // Now don't proceed.
959   interstitial->DontProceed();
960   EXPECT_TRUE(deleted);
961   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
962   EXPECT_FALSE(contents()->showing_interstitial_page());
963   EXPECT_TRUE(contents()->interstitial_page() == NULL);
964   entry = controller().GetActiveEntry();
965   ASSERT_TRUE(entry != NULL);
966   EXPECT_TRUE(entry->url() == url1);
967   EXPECT_EQ(1, controller().entry_count());
968 }
969 
970 // Test navigating to a page that shows an interstitial without creating a new
971 // navigation entry (this happens when the interstitial is triggered by a
972 // sub-resource in the page), then hiding it without proceeding.
TEST_F(TabContentsTest,ShowInterstitialNoNewNavigationDontProceed)973 TEST_F(TabContentsTest, ShowInterstitialNoNewNavigationDontProceed) {
974   // Navigate to a page.
975   GURL url1("http://www.google.com");
976   rvh()->SendNavigate(1, url1);
977   EXPECT_EQ(1, controller().entry_count());
978 
979   // Show an interstitial.
980   TestInterstitialPage::InterstitialState state =
981       TestInterstitialPage::UNDECIDED;
982   bool deleted = false;
983   GURL url2("http://interstitial");
984   TestInterstitialPage* interstitial =
985       new TestInterstitialPage(contents(), false, url2, &state, &deleted);
986   TestInterstitialPageStateGuard state_guard(interstitial);
987   interstitial->Show();
988   // The interstitial should not show until its navigation has committed.
989   EXPECT_FALSE(interstitial->is_showing());
990   EXPECT_FALSE(contents()->showing_interstitial_page());
991   EXPECT_TRUE(contents()->interstitial_page() == NULL);
992   // Let's commit the interstitial navigation.
993   interstitial->TestDidNavigate(1, url2);
994   EXPECT_TRUE(interstitial->is_showing());
995   EXPECT_TRUE(contents()->showing_interstitial_page());
996   EXPECT_TRUE(contents()->interstitial_page() == interstitial);
997   NavigationEntry* entry = controller().GetActiveEntry();
998   ASSERT_TRUE(entry != NULL);
999   // The URL specified to the interstitial should have been ignored.
1000   EXPECT_TRUE(entry->url() == url1);
1001 
1002   // Now don't proceed.
1003   interstitial->DontProceed();
1004   EXPECT_TRUE(deleted);
1005   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1006   EXPECT_FALSE(contents()->showing_interstitial_page());
1007   EXPECT_TRUE(contents()->interstitial_page() == NULL);
1008   entry = controller().GetActiveEntry();
1009   ASSERT_TRUE(entry != NULL);
1010   EXPECT_TRUE(entry->url() == url1);
1011   EXPECT_EQ(1, controller().entry_count());
1012 }
1013 
1014 // Test navigating to a page (with the navigation initiated from the browser,
1015 // as when a URL is typed in the location bar) that shows an interstitial and
1016 // creates a new navigation entry, then proceeding.
TEST_F(TabContentsTest,ShowInterstitialFromBrowserNewNavigationProceed)1017 TEST_F(TabContentsTest,
1018        ShowInterstitialFromBrowserNewNavigationProceed) {
1019   // Navigate to a page.
1020   GURL url1("http://www.google.com");
1021   rvh()->SendNavigate(1, url1);
1022   EXPECT_EQ(1, controller().entry_count());
1023 
1024   // Initiate a browser navigation that will trigger the interstitial
1025   controller().LoadURL(GURL("http://www.evil.com"), GURL(),
1026                         PageTransition::TYPED);
1027 
1028   // Show an interstitial.
1029   TestInterstitialPage::InterstitialState state =
1030       TestInterstitialPage::UNDECIDED;
1031   bool deleted = false;
1032   GURL url2("http://interstitial");
1033   TestInterstitialPage* interstitial =
1034       new TestInterstitialPage(contents(), true, url2, &state, &deleted);
1035   TestInterstitialPageStateGuard state_guard(interstitial);
1036   interstitial->Show();
1037   // The interstitial should not show until its navigation has committed.
1038   EXPECT_FALSE(interstitial->is_showing());
1039   EXPECT_FALSE(contents()->showing_interstitial_page());
1040   EXPECT_TRUE(contents()->interstitial_page() == NULL);
1041   // Let's commit the interstitial navigation.
1042   interstitial->TestDidNavigate(1, url2);
1043   EXPECT_TRUE(interstitial->is_showing());
1044   EXPECT_TRUE(contents()->showing_interstitial_page());
1045   EXPECT_TRUE(contents()->interstitial_page() == interstitial);
1046   NavigationEntry* entry = controller().GetActiveEntry();
1047   ASSERT_TRUE(entry != NULL);
1048   EXPECT_TRUE(entry->url() == url2);
1049 
1050   // Then proceed.
1051   interstitial->Proceed();
1052   // The interstitial should show until the new navigation commits.
1053   ASSERT_FALSE(deleted);
1054   EXPECT_EQ(TestInterstitialPage::OKED, state);
1055   EXPECT_TRUE(contents()->showing_interstitial_page());
1056   EXPECT_TRUE(contents()->interstitial_page() == interstitial);
1057 
1058   // Simulate the navigation to the page, that's when the interstitial gets
1059   // hidden.
1060   GURL url3("http://www.thepage.com");
1061   rvh()->SendNavigate(2, url3);
1062 
1063   EXPECT_TRUE(deleted);
1064   EXPECT_FALSE(contents()->showing_interstitial_page());
1065   EXPECT_TRUE(contents()->interstitial_page() == NULL);
1066   entry = controller().GetActiveEntry();
1067   ASSERT_TRUE(entry != NULL);
1068   EXPECT_TRUE(entry->url() == url3);
1069 
1070   EXPECT_EQ(2, controller().entry_count());
1071 }
1072 
1073 // Test navigating to a page (with the navigation initiated from the renderer,
1074 // as when clicking on a link in the page) that shows an interstitial and
1075 // creates a new navigation entry, then proceeding.
TEST_F(TabContentsTest,ShowInterstitialFromRendererNewNavigationProceed)1076 TEST_F(TabContentsTest,
1077        ShowInterstitialFromRendererNewNavigationProceed) {
1078   // Navigate to a page.
1079   GURL url1("http://www.google.com");
1080   rvh()->SendNavigate(1, url1);
1081   EXPECT_EQ(1, controller().entry_count());
1082 
1083   // Show an interstitial.
1084   TestInterstitialPage::InterstitialState state =
1085       TestInterstitialPage::UNDECIDED;
1086   bool deleted = false;
1087   GURL url2("http://interstitial");
1088   TestInterstitialPage* interstitial =
1089       new TestInterstitialPage(contents(), true, url2, &state, &deleted);
1090   TestInterstitialPageStateGuard state_guard(interstitial);
1091   interstitial->Show();
1092   // The interstitial should not show until its navigation has committed.
1093   EXPECT_FALSE(interstitial->is_showing());
1094   EXPECT_FALSE(contents()->showing_interstitial_page());
1095   EXPECT_TRUE(contents()->interstitial_page() == NULL);
1096   // Let's commit the interstitial navigation.
1097   interstitial->TestDidNavigate(1, url2);
1098   EXPECT_TRUE(interstitial->is_showing());
1099   EXPECT_TRUE(contents()->showing_interstitial_page());
1100   EXPECT_TRUE(contents()->interstitial_page() == interstitial);
1101   NavigationEntry* entry = controller().GetActiveEntry();
1102   ASSERT_TRUE(entry != NULL);
1103   EXPECT_TRUE(entry->url() == url2);
1104 
1105   // Then proceed.
1106   interstitial->Proceed();
1107   // The interstitial should show until the new navigation commits.
1108   ASSERT_FALSE(deleted);
1109   EXPECT_EQ(TestInterstitialPage::OKED, state);
1110   EXPECT_TRUE(contents()->showing_interstitial_page());
1111   EXPECT_TRUE(contents()->interstitial_page() == interstitial);
1112 
1113   // Simulate the navigation to the page, that's when the interstitial gets
1114   // hidden.
1115   GURL url3("http://www.thepage.com");
1116   rvh()->SendNavigate(2, url3);
1117 
1118   EXPECT_TRUE(deleted);
1119   EXPECT_FALSE(contents()->showing_interstitial_page());
1120   EXPECT_TRUE(contents()->interstitial_page() == NULL);
1121   entry = controller().GetActiveEntry();
1122   ASSERT_TRUE(entry != NULL);
1123   EXPECT_TRUE(entry->url() == url3);
1124 
1125   EXPECT_EQ(2, controller().entry_count());
1126 }
1127 
1128 // Test navigating to a page that shows an interstitial without creating a new
1129 // navigation entry (this happens when the interstitial is triggered by a
1130 // sub-resource in the page), then proceeding.
TEST_F(TabContentsTest,ShowInterstitialNoNewNavigationProceed)1131 TEST_F(TabContentsTest, ShowInterstitialNoNewNavigationProceed) {
1132   // Navigate to a page so we have a navigation entry in the controller.
1133   GURL url1("http://www.google.com");
1134   rvh()->SendNavigate(1, url1);
1135   EXPECT_EQ(1, controller().entry_count());
1136 
1137   // Show an interstitial.
1138   TestInterstitialPage::InterstitialState state =
1139       TestInterstitialPage::UNDECIDED;
1140   bool deleted = false;
1141   GURL url2("http://interstitial");
1142   TestInterstitialPage* interstitial =
1143       new TestInterstitialPage(contents(), false, url2, &state, &deleted);
1144   TestInterstitialPageStateGuard state_guard(interstitial);
1145   interstitial->Show();
1146   // The interstitial should not show until its navigation has committed.
1147   EXPECT_FALSE(interstitial->is_showing());
1148   EXPECT_FALSE(contents()->showing_interstitial_page());
1149   EXPECT_TRUE(contents()->interstitial_page() == NULL);
1150   // Let's commit the interstitial navigation.
1151   interstitial->TestDidNavigate(1, url2);
1152   EXPECT_TRUE(interstitial->is_showing());
1153   EXPECT_TRUE(contents()->showing_interstitial_page());
1154   EXPECT_TRUE(contents()->interstitial_page() == interstitial);
1155   NavigationEntry* entry = controller().GetActiveEntry();
1156   ASSERT_TRUE(entry != NULL);
1157   // The URL specified to the interstitial should have been ignored.
1158   EXPECT_TRUE(entry->url() == url1);
1159 
1160   // Then proceed.
1161   interstitial->Proceed();
1162   // Since this is not a new navigation, the previous page is dismissed right
1163   // away and shows the original page.
1164   EXPECT_TRUE(deleted);
1165   EXPECT_EQ(TestInterstitialPage::OKED, state);
1166   EXPECT_FALSE(contents()->showing_interstitial_page());
1167   EXPECT_TRUE(contents()->interstitial_page() == NULL);
1168   entry = controller().GetActiveEntry();
1169   ASSERT_TRUE(entry != NULL);
1170   EXPECT_TRUE(entry->url() == url1);
1171 
1172   EXPECT_EQ(1, controller().entry_count());
1173 }
1174 
1175 // Test navigating to a page that shows an interstitial, then navigating away.
TEST_F(TabContentsTest,ShowInterstitialThenNavigate)1176 TEST_F(TabContentsTest, ShowInterstitialThenNavigate) {
1177   // Show interstitial.
1178   TestInterstitialPage::InterstitialState state =
1179       TestInterstitialPage::UNDECIDED;
1180   bool deleted = false;
1181   GURL url("http://interstitial");
1182   TestInterstitialPage* interstitial =
1183       new TestInterstitialPage(contents(), true, url, &state, &deleted);
1184   TestInterstitialPageStateGuard state_guard(interstitial);
1185   interstitial->Show();
1186   interstitial->TestDidNavigate(1, url);
1187 
1188   // While interstitial showing, navigate to a new URL.
1189   const GURL url2("http://www.yahoo.com");
1190   rvh()->SendNavigate(1, url2);
1191 
1192   EXPECT_TRUE(deleted);
1193   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1194 }
1195 
1196 // Test navigating to a page that shows an interstitial, then going back.
TEST_F(TabContentsTest,ShowInterstitialThenGoBack)1197 TEST_F(TabContentsTest, ShowInterstitialThenGoBack) {
1198   // Navigate to a page so we have a navigation entry in the controller.
1199   GURL url1("http://www.google.com");
1200   rvh()->SendNavigate(1, url1);
1201   EXPECT_EQ(1, controller().entry_count());
1202 
1203   // Show interstitial.
1204   TestInterstitialPage::InterstitialState state =
1205       TestInterstitialPage::UNDECIDED;
1206   bool deleted = false;
1207   GURL interstitial_url("http://interstitial");
1208   TestInterstitialPage* interstitial =
1209       new TestInterstitialPage(contents(), true, interstitial_url,
1210                                &state, &deleted);
1211   TestInterstitialPageStateGuard state_guard(interstitial);
1212   interstitial->Show();
1213   interstitial->TestDidNavigate(2, interstitial_url);
1214 
1215   // While the interstitial is showing, go back.
1216   controller().GoBack();
1217   rvh()->SendNavigate(1, url1);
1218 
1219   // Make sure we are back to the original page and that the interstitial is
1220   // gone.
1221   EXPECT_TRUE(deleted);
1222   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1223   NavigationEntry* entry = controller().GetActiveEntry();
1224   ASSERT_TRUE(entry);
1225   EXPECT_EQ(url1.spec(), entry->url().spec());
1226 }
1227 
1228 // Test navigating to a page that shows an interstitial, has a renderer crash,
1229 // and then goes back.
TEST_F(TabContentsTest,ShowInterstitialCrashRendererThenGoBack)1230 TEST_F(TabContentsTest, ShowInterstitialCrashRendererThenGoBack) {
1231   // Navigate to a page so we have a navigation entry in the controller.
1232   GURL url1("http://www.google.com");
1233   rvh()->SendNavigate(1, url1);
1234   EXPECT_EQ(1, controller().entry_count());
1235 
1236   // Show interstitial.
1237   TestInterstitialPage::InterstitialState state =
1238       TestInterstitialPage::UNDECIDED;
1239   bool deleted = false;
1240   GURL interstitial_url("http://interstitial");
1241   TestInterstitialPage* interstitial =
1242       new TestInterstitialPage(contents(), true, interstitial_url,
1243                                &state, &deleted);
1244   TestInterstitialPageStateGuard state_guard(interstitial);
1245   interstitial->Show();
1246   interstitial->TestDidNavigate(2, interstitial_url);
1247 
1248   // Crash the renderer
1249   rvh()->TestOnMessageReceived(
1250       ViewHostMsg_RenderViewGone(
1251           0, base::TERMINATION_STATUS_PROCESS_CRASHED, -1));
1252 
1253   // While the interstitial is showing, go back.
1254   controller().GoBack();
1255   rvh()->SendNavigate(1, url1);
1256 
1257   // Make sure we are back to the original page and that the interstitial is
1258   // gone.
1259   EXPECT_TRUE(deleted);
1260   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1261   NavigationEntry* entry = controller().GetActiveEntry();
1262   ASSERT_TRUE(entry);
1263   EXPECT_EQ(url1.spec(), entry->url().spec());
1264 }
1265 
1266 // Test navigating to a page that shows an interstitial, has the renderer crash,
1267 // and then navigates to the interstitial.
TEST_F(TabContentsTest,ShowInterstitialCrashRendererThenNavigate)1268 TEST_F(TabContentsTest, ShowInterstitialCrashRendererThenNavigate) {
1269   // Navigate to a page so we have a navigation entry in the controller.
1270   GURL url1("http://www.google.com");
1271   rvh()->SendNavigate(1, url1);
1272   EXPECT_EQ(1, controller().entry_count());
1273 
1274   // Show interstitial.
1275   TestInterstitialPage::InterstitialState state =
1276       TestInterstitialPage::UNDECIDED;
1277   bool deleted = false;
1278   GURL interstitial_url("http://interstitial");
1279   TestInterstitialPage* interstitial =
1280       new TestInterstitialPage(contents(), true, interstitial_url,
1281                                &state, &deleted);
1282   TestInterstitialPageStateGuard state_guard(interstitial);
1283   interstitial->Show();
1284 
1285   // Crash the renderer
1286   rvh()->TestOnMessageReceived(
1287       ViewHostMsg_RenderViewGone(
1288           0, base::TERMINATION_STATUS_PROCESS_CRASHED, -1));
1289 
1290   interstitial->TestDidNavigate(2, interstitial_url);
1291 }
1292 
1293 // Test navigating to a page that shows an interstitial, then close the tab.
TEST_F(TabContentsTest,ShowInterstitialThenCloseTab)1294 TEST_F(TabContentsTest, ShowInterstitialThenCloseTab) {
1295   // Show interstitial.
1296   TestInterstitialPage::InterstitialState state =
1297       TestInterstitialPage::UNDECIDED;
1298   bool deleted = false;
1299   GURL url("http://interstitial");
1300   TestInterstitialPage* interstitial =
1301       new TestInterstitialPage(contents(), true, url, &state, &deleted);
1302   TestInterstitialPageStateGuard state_guard(interstitial);
1303   interstitial->Show();
1304   interstitial->TestDidNavigate(1, url);
1305 
1306   // Now close the tab.
1307   DeleteContents();
1308   EXPECT_TRUE(deleted);
1309   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1310 }
1311 
1312 // Test that after Proceed is called and an interstitial is still shown, no more
1313 // commands get executed.
TEST_F(TabContentsTest,ShowInterstitialProceedMultipleCommands)1314 TEST_F(TabContentsTest, ShowInterstitialProceedMultipleCommands) {
1315   // Navigate to a page so we have a navigation entry in the controller.
1316   GURL url1("http://www.google.com");
1317   rvh()->SendNavigate(1, url1);
1318   EXPECT_EQ(1, controller().entry_count());
1319 
1320   // Show an interstitial.
1321   TestInterstitialPage::InterstitialState state =
1322       TestInterstitialPage::UNDECIDED;
1323   bool deleted = false;
1324   GURL url2("http://interstitial");
1325   TestInterstitialPage* interstitial =
1326       new TestInterstitialPage(contents(), true, url2, &state, &deleted);
1327   TestInterstitialPageStateGuard state_guard(interstitial);
1328   interstitial->Show();
1329   interstitial->TestDidNavigate(1, url2);
1330 
1331   // Run a command.
1332   EXPECT_EQ(0, interstitial->command_received_count());
1333   interstitial->TestDomOperationResponse("toto");
1334   EXPECT_EQ(1, interstitial->command_received_count());
1335 
1336   // Then proceed.
1337   interstitial->Proceed();
1338   ASSERT_FALSE(deleted);
1339 
1340   // While the navigation to the new page is pending, send other commands, they
1341   // should be ignored.
1342   interstitial->TestDomOperationResponse("hello");
1343   interstitial->TestDomOperationResponse("hi");
1344   EXPECT_EQ(1, interstitial->command_received_count());
1345 }
1346 
1347 // Test showing an interstitial while another interstitial is already showing.
TEST_F(TabContentsTest,ShowInterstitialOnInterstitial)1348 TEST_F(TabContentsTest, ShowInterstitialOnInterstitial) {
1349   // Navigate to a page so we have a navigation entry in the controller.
1350   GURL start_url("http://www.google.com");
1351   rvh()->SendNavigate(1, start_url);
1352   EXPECT_EQ(1, controller().entry_count());
1353 
1354   // Show an interstitial.
1355   TestInterstitialPage::InterstitialState state1 =
1356       TestInterstitialPage::UNDECIDED;
1357   bool deleted1 = false;
1358   GURL url1("http://interstitial1");
1359   TestInterstitialPage* interstitial1 =
1360       new TestInterstitialPage(contents(), true, url1, &state1, &deleted1);
1361   TestInterstitialPageStateGuard state_guard1(interstitial1);
1362   interstitial1->Show();
1363   interstitial1->TestDidNavigate(1, url1);
1364 
1365   // Now show another interstitial.
1366   TestInterstitialPage::InterstitialState state2 =
1367       TestInterstitialPage::UNDECIDED;
1368   bool deleted2 = false;
1369   GURL url2("http://interstitial2");
1370   TestInterstitialPage* interstitial2 =
1371       new TestInterstitialPage(contents(), true, url2, &state2, &deleted2);
1372   TestInterstitialPageStateGuard state_guard2(interstitial2);
1373   interstitial2->Show();
1374   interstitial2->TestDidNavigate(1, url2);
1375 
1376   // Showing interstitial2 should have caused interstitial1 to go away.
1377   EXPECT_TRUE(deleted1);
1378   EXPECT_EQ(TestInterstitialPage::CANCELED, state1);
1379 
1380   // Let's make sure interstitial2 is working as intended.
1381   ASSERT_FALSE(deleted2);
1382   EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2);
1383   interstitial2->Proceed();
1384   GURL landing_url("http://www.thepage.com");
1385   rvh()->SendNavigate(2, landing_url);
1386 
1387   EXPECT_TRUE(deleted2);
1388   EXPECT_FALSE(contents()->showing_interstitial_page());
1389   EXPECT_TRUE(contents()->interstitial_page() == NULL);
1390   NavigationEntry* entry = controller().GetActiveEntry();
1391   ASSERT_TRUE(entry != NULL);
1392   EXPECT_TRUE(entry->url() == landing_url);
1393   EXPECT_EQ(2, controller().entry_count());
1394 }
1395 
1396 // Test showing an interstitial, proceeding and then navigating to another
1397 // interstitial.
TEST_F(TabContentsTest,ShowInterstitialProceedShowInterstitial)1398 TEST_F(TabContentsTest, ShowInterstitialProceedShowInterstitial) {
1399   // Navigate to a page so we have a navigation entry in the controller.
1400   GURL start_url("http://www.google.com");
1401   rvh()->SendNavigate(1, start_url);
1402   EXPECT_EQ(1, controller().entry_count());
1403 
1404   // Show an interstitial.
1405   TestInterstitialPage::InterstitialState state1 =
1406       TestInterstitialPage::UNDECIDED;
1407   bool deleted1 = false;
1408   GURL url1("http://interstitial1");
1409   TestInterstitialPage* interstitial1 =
1410       new TestInterstitialPage(contents(), true, url1, &state1, &deleted1);
1411   TestInterstitialPageStateGuard state_guard1(interstitial1);
1412   interstitial1->Show();
1413   interstitial1->TestDidNavigate(1, url1);
1414 
1415   // Take action.  The interstitial won't be hidden until the navigation is
1416   // committed.
1417   interstitial1->Proceed();
1418   EXPECT_EQ(TestInterstitialPage::OKED, state1);
1419 
1420   // Now show another interstitial (simulating the navigation causing another
1421   // interstitial).
1422   TestInterstitialPage::InterstitialState state2 =
1423       TestInterstitialPage::UNDECIDED;
1424   bool deleted2 = false;
1425   GURL url2("http://interstitial2");
1426   TestInterstitialPage* interstitial2 =
1427       new TestInterstitialPage(contents(), true, url2, &state2, &deleted2);
1428   TestInterstitialPageStateGuard state_guard2(interstitial2);
1429   interstitial2->Show();
1430   interstitial2->TestDidNavigate(1, url2);
1431 
1432   // Showing interstitial2 should have caused interstitial1 to go away.
1433   EXPECT_TRUE(deleted1);
1434 
1435   // Let's make sure interstitial2 is working as intended.
1436   ASSERT_FALSE(deleted2);
1437   EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2);
1438   interstitial2->Proceed();
1439   GURL landing_url("http://www.thepage.com");
1440   rvh()->SendNavigate(2, landing_url);
1441 
1442   EXPECT_TRUE(deleted2);
1443   EXPECT_FALSE(contents()->showing_interstitial_page());
1444   EXPECT_TRUE(contents()->interstitial_page() == NULL);
1445   NavigationEntry* entry = controller().GetActiveEntry();
1446   ASSERT_TRUE(entry != NULL);
1447   EXPECT_TRUE(entry->url() == landing_url);
1448   EXPECT_EQ(2, controller().entry_count());
1449 }
1450 
1451 // Test that navigating away from an interstitial while it's loading cause it
1452 // not to show.
TEST_F(TabContentsTest,NavigateBeforeInterstitialShows)1453 TEST_F(TabContentsTest, NavigateBeforeInterstitialShows) {
1454   // Show an interstitial.
1455   TestInterstitialPage::InterstitialState state =
1456       TestInterstitialPage::UNDECIDED;
1457   bool deleted = false;
1458   GURL interstitial_url("http://interstitial");
1459   TestInterstitialPage* interstitial =
1460       new TestInterstitialPage(contents(), true, interstitial_url,
1461                                &state, &deleted);
1462   TestInterstitialPageStateGuard state_guard(interstitial);
1463   interstitial->Show();
1464 
1465   // Let's simulate a navigation initiated from the browser before the
1466   // interstitial finishes loading.
1467   const GURL url("http://www.google.com");
1468   controller().LoadURL(url, GURL(), PageTransition::TYPED);
1469   ASSERT_FALSE(deleted);
1470   EXPECT_FALSE(interstitial->is_showing());
1471 
1472   // Now let's make the interstitial navigation commit.
1473   interstitial->TestDidNavigate(1, interstitial_url);
1474 
1475   // After it loaded the interstitial should be gone.
1476   EXPECT_TRUE(deleted);
1477   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1478 }
1479 
1480 // Test that a new request to show an interstitial while an interstitial is
1481 // pending does not cause problems. htp://crbug/29655 and htp://crbug/9442.
TEST_F(TabContentsTest,TwoQuickInterstitials)1482 TEST_F(TabContentsTest, TwoQuickInterstitials) {
1483   GURL interstitial_url("http://interstitial");
1484 
1485   // Show a first interstitial.
1486   TestInterstitialPage::InterstitialState state1 =
1487       TestInterstitialPage::UNDECIDED;
1488   bool deleted1 = false;
1489   TestInterstitialPage* interstitial1 =
1490       new TestInterstitialPage(contents(), true, interstitial_url,
1491                                &state1, &deleted1);
1492   TestInterstitialPageStateGuard state_guard1(interstitial1);
1493   interstitial1->Show();
1494 
1495   // Show another interstitial on that same tab before the first one had time
1496   // to load.
1497   TestInterstitialPage::InterstitialState state2 =
1498       TestInterstitialPage::UNDECIDED;
1499   bool deleted2 = false;
1500   TestInterstitialPage* interstitial2 =
1501       new TestInterstitialPage(contents(), true, interstitial_url,
1502                                &state2, &deleted2);
1503   TestInterstitialPageStateGuard state_guard2(interstitial2);
1504   interstitial2->Show();
1505 
1506   // The first interstitial should have been closed and deleted.
1507   EXPECT_TRUE(deleted1);
1508   EXPECT_EQ(TestInterstitialPage::CANCELED, state1);
1509 
1510   // The 2nd one should still be OK.
1511   ASSERT_FALSE(deleted2);
1512   EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2);
1513 
1514   // Make the interstitial navigation commit it should be showing.
1515   interstitial2->TestDidNavigate(1, interstitial_url);
1516   EXPECT_EQ(interstitial2, contents()->interstitial_page());
1517 }
1518 
1519 // Test showing an interstitial and have its renderer crash.
TEST_F(TabContentsTest,InterstitialCrasher)1520 TEST_F(TabContentsTest, InterstitialCrasher) {
1521   // Show an interstitial.
1522   TestInterstitialPage::InterstitialState state =
1523       TestInterstitialPage::UNDECIDED;
1524   bool deleted = false;
1525   GURL url("http://interstitial");
1526   TestInterstitialPage* interstitial =
1527       new TestInterstitialPage(contents(), true, url, &state, &deleted);
1528   TestInterstitialPageStateGuard state_guard(interstitial);
1529   interstitial->Show();
1530   // Simulate a renderer crash before the interstitial is shown.
1531   interstitial->TestRenderViewGone(
1532       base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
1533   // The interstitial should have been dismissed.
1534   EXPECT_TRUE(deleted);
1535   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1536 
1537   // Now try again but this time crash the intersitial after it was shown.
1538   interstitial =
1539       new TestInterstitialPage(contents(), true, url, &state, &deleted);
1540   interstitial->Show();
1541   interstitial->TestDidNavigate(1, url);
1542   // Simulate a renderer crash.
1543   interstitial->TestRenderViewGone(
1544       base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
1545   // The interstitial should have been dismissed.
1546   EXPECT_TRUE(deleted);
1547   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1548 }
1549 
1550 // Tests that showing an interstitial as a result of a browser initiated
1551 // navigation while an interstitial is showing does not remove the pending
1552 // entry (see http://crbug.com/9791).
TEST_F(TabContentsTest,NewInterstitialDoesNotCancelPendingEntry)1553 TEST_F(TabContentsTest, NewInterstitialDoesNotCancelPendingEntry) {
1554   const char kUrl[] = "http://www.badguys.com/";
1555   const GURL kGURL(kUrl);
1556 
1557   // Start a navigation to a page
1558   contents()->controller().LoadURL(kGURL, GURL(), PageTransition::TYPED);
1559 
1560   // Simulate that navigation triggering an interstitial.
1561   TestInterstitialPage::InterstitialState state =
1562       TestInterstitialPage::UNDECIDED;
1563   bool deleted = false;
1564   TestInterstitialPage* interstitial =
1565       new TestInterstitialPage(contents(), true, kGURL, &state, &deleted);
1566   TestInterstitialPageStateGuard state_guard(interstitial);
1567   interstitial->Show();
1568   interstitial->TestDidNavigate(1, kGURL);
1569 
1570   // Initiate a new navigation from the browser that also triggers an
1571   // interstitial.
1572   contents()->controller().LoadURL(kGURL, GURL(), PageTransition::TYPED);
1573   TestInterstitialPage::InterstitialState state2 =
1574       TestInterstitialPage::UNDECIDED;
1575   bool deleted2 = false;
1576   TestInterstitialPage* interstitial2 =
1577       new TestInterstitialPage(contents(), true, kGURL, &state, &deleted);
1578   TestInterstitialPageStateGuard state_guard2(interstitial2);
1579   interstitial2->Show();
1580   interstitial2->TestDidNavigate(1, kGURL);
1581 
1582   // Make sure we still have an entry.
1583   NavigationEntry* entry = contents()->controller().pending_entry();
1584   ASSERT_TRUE(entry);
1585   EXPECT_EQ(kUrl, entry->url().spec());
1586 
1587   // And that the first interstitial is gone, but not the second.
1588   EXPECT_TRUE(deleted);
1589   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1590   EXPECT_FALSE(deleted2);
1591   EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2);
1592 }
1593 
1594 // Tests that Javascript messages are not shown while an interstitial is
1595 // showing.
TEST_F(TabContentsTest,NoJSMessageOnInterstitials)1596 TEST_F(TabContentsTest, NoJSMessageOnInterstitials) {
1597   const char kUrl[] = "http://www.badguys.com/";
1598   const GURL kGURL(kUrl);
1599 
1600   // Start a navigation to a page
1601   contents()->controller().LoadURL(kGURL, GURL(), PageTransition::TYPED);
1602   // DidNavigate from the page
1603   ViewHostMsg_FrameNavigate_Params params;
1604   InitNavigateParams(&params, 1, kGURL);
1605   contents()->TestDidNavigate(rvh(), params);
1606 
1607   // Simulate showing an interstitial while the page is showing.
1608   TestInterstitialPage::InterstitialState state =
1609       TestInterstitialPage::UNDECIDED;
1610   bool deleted = false;
1611   TestInterstitialPage* interstitial =
1612       new TestInterstitialPage(contents(), true, kGURL, &state, &deleted);
1613   TestInterstitialPageStateGuard state_guard(interstitial);
1614   interstitial->Show();
1615   interstitial->TestDidNavigate(1, kGURL);
1616 
1617   // While the interstitial is showing, let's simulate the hidden page
1618   // attempting to show a JS message.
1619   IPC::Message* dummy_message = new IPC::Message;
1620   bool did_suppress_message = false;
1621   contents()->RunJavaScriptMessage(L"This is an informative message", L"OK",
1622       kGURL, ui::MessageBoxFlags::kIsJavascriptAlert, dummy_message,
1623       &did_suppress_message);
1624   EXPECT_TRUE(did_suppress_message);
1625 }
1626 
1627 // Makes sure that if the source passed to CopyStateFromAndPrune has an
1628 // interstitial it isn't copied over to the destination.
TEST_F(TabContentsTest,CopyStateFromAndPruneSourceInterstitial)1629 TEST_F(TabContentsTest, CopyStateFromAndPruneSourceInterstitial) {
1630   // Navigate to a page.
1631   GURL url1("http://www.google.com");
1632   rvh()->SendNavigate(1, url1);
1633   EXPECT_EQ(1, controller().entry_count());
1634 
1635   // Initiate a browser navigation that will trigger the interstitial
1636   controller().LoadURL(GURL("http://www.evil.com"), GURL(),
1637                         PageTransition::TYPED);
1638 
1639   // Show an interstitial.
1640   TestInterstitialPage::InterstitialState state =
1641       TestInterstitialPage::UNDECIDED;
1642   bool deleted = false;
1643   GURL url2("http://interstitial");
1644   TestInterstitialPage* interstitial =
1645       new TestInterstitialPage(contents(), true, url2, &state, &deleted);
1646   TestInterstitialPageStateGuard state_guard(interstitial);
1647   interstitial->Show();
1648   interstitial->TestDidNavigate(1, url2);
1649   EXPECT_TRUE(interstitial->is_showing());
1650   EXPECT_EQ(2, controller().entry_count());
1651 
1652   // Create another NavigationController.
1653   GURL url3("http://foo2");
1654   scoped_ptr<TestTabContents> other_contents(CreateTestTabContents());
1655   NavigationController& other_controller = other_contents->controller();
1656   other_contents->NavigateAndCommit(url3);
1657   other_controller.CopyStateFromAndPrune(&controller(), false);
1658 
1659   // The merged controller should only have two entries: url1 and url2.
1660   ASSERT_EQ(2, other_controller.entry_count());
1661   EXPECT_EQ(1, other_controller.GetCurrentEntryIndex());
1662   EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->url());
1663   EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->url());
1664 
1665   // And the merged controller shouldn't be showing an interstitial.
1666   EXPECT_FALSE(other_contents->showing_interstitial_page());
1667 }
1668 
1669 // Makes sure that CopyStateFromAndPrune does the right thing if the object
1670 // CopyStateFromAndPrune is invoked on is showing an interstitial.
TEST_F(TabContentsTest,CopyStateFromAndPruneTargetInterstitial)1671 TEST_F(TabContentsTest, CopyStateFromAndPruneTargetInterstitial) {
1672   // Navigate to a page.
1673   GURL url1("http://www.google.com");
1674   contents()->NavigateAndCommit(url1);
1675 
1676   // Create another NavigationController.
1677   scoped_ptr<TestTabContents> other_contents(CreateTestTabContents());
1678   NavigationController& other_controller = other_contents->controller();
1679 
1680   // Navigate it to url2.
1681   GURL url2("http://foo2");
1682   other_contents->NavigateAndCommit(url2);
1683 
1684   // Show an interstitial.
1685   TestInterstitialPage::InterstitialState state =
1686       TestInterstitialPage::UNDECIDED;
1687   bool deleted = false;
1688   GURL url3("http://interstitial");
1689   TestInterstitialPage* interstitial =
1690       new TestInterstitialPage(other_contents.get(), true, url3, &state,
1691                                &deleted);
1692   TestInterstitialPageStateGuard state_guard(interstitial);
1693   interstitial->Show();
1694   interstitial->TestDidNavigate(1, url3);
1695   EXPECT_TRUE(interstitial->is_showing());
1696   EXPECT_EQ(2, other_controller.entry_count());
1697 
1698   other_controller.CopyStateFromAndPrune(&controller(), false);
1699 
1700   // The merged controller should only have two entries: url1 and url2.
1701   ASSERT_EQ(2, other_controller.entry_count());
1702   EXPECT_EQ(1, other_controller.GetCurrentEntryIndex());
1703   EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->url());
1704   EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->url());
1705 
1706   // It should have a transient entry.
1707   EXPECT_TRUE(other_controller.GetTransientEntry());
1708 
1709   // And the interstitial should be showing.
1710   EXPECT_TRUE(other_contents->showing_interstitial_page());
1711 
1712   // And the interstitial should do a reload on don't proceed.
1713   EXPECT_TRUE(other_contents->interstitial_page()->reload_on_dont_proceed());
1714 }
1715 
1716 class ConstrainedWindowCloseTest : public ConstrainedWindow {
1717  public:
ConstrainedWindowCloseTest(TabContents * tab_contents)1718   explicit ConstrainedWindowCloseTest(TabContents* tab_contents)
1719       : tab_contents_(tab_contents) {
1720   }
1721 
ShowConstrainedWindow()1722   virtual void ShowConstrainedWindow() {}
FocusConstrainedWindow()1723   virtual void FocusConstrainedWindow() {}
~ConstrainedWindowCloseTest()1724   virtual ~ConstrainedWindowCloseTest() {}
1725 
CloseConstrainedWindow()1726   virtual void CloseConstrainedWindow() {
1727     tab_contents_->WillClose(this);
1728     close_count++;
1729   }
1730 
1731   int close_count;
1732   TabContents* tab_contents_;
1733 };
1734 
TEST_F(TabContentsTest,ConstrainedWindows)1735 TEST_F(TabContentsTest, ConstrainedWindows) {
1736   TabContents* tab_contents = CreateTestTabContents();
1737   ConstrainedWindowCloseTest window(tab_contents);
1738   window.close_count = 0;
1739 
1740   const int kWindowCount = 4;
1741   for (int i = 0; i < kWindowCount; i++) {
1742     tab_contents->AddConstrainedDialog(&window);
1743   }
1744   EXPECT_EQ(window.close_count, 0);
1745   delete tab_contents;
1746   EXPECT_EQ(window.close_count, kWindowCount);
1747 }
1748