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
34using webkit_glue::PasswordForm;
35
36static 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
55class 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:
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.
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
95  virtual ~TestInterstitialPage() {
96    if (deleted_)
97      *deleted_ = true;
98    if (delegate_)
99      delegate_->TestInterstitialPageDeleted(this);
100  }
101
102  virtual void DontProceed() {
103    if (state_)
104      *state_ = CANCELED;
105    InterstitialPage::DontProceed();
106  }
107  virtual void Proceed() {
108    if (state_)
109      *state_ = OKED;
110    InterstitialPage::Proceed();
111  }
112
113  int command_received_count() const {
114    return command_received_count_;
115  }
116
117  void TestDomOperationResponse(const std::string& json_string) {
118    DomOperationResponse(json_string, 1);
119  }
120
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
127  void TestRenderViewGone(base::TerminationStatus status, int error_code) {
128    RenderViewGone(render_view_host(), status, error_code);
129  }
130
131  bool is_showing() const {
132    return static_cast<TestRenderWidgetHostView*>(render_view_host()->view())->
133        is_showing();
134  }
135
136  void ClearStates() {
137    state_ = NULL;
138    deleted_ = NULL;
139    delegate_ = NULL;
140  }
141
142  void set_delegate(Delegate* delegate) {
143    delegate_ = delegate;
144  }
145
146 protected:
147  virtual RenderViewHost* CreateRenderViewHost() {
148    return new TestRenderViewHost(
149        SiteInstance::CreateSiteInstance(tab()->profile()),
150        this, MSG_ROUTING_NONE);
151  }
152
153  virtual TabContentsView* CreateTabContentsView() { return NULL; }
154
155
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
167class TestInterstitialPageStateGuard : public TestInterstitialPage::Delegate {
168 public:
169  explicit TestInterstitialPageStateGuard(
170      TestInterstitialPage* interstitial_page)
171      : interstitial_page_(interstitial_page) {
172    DCHECK(interstitial_page_);
173    interstitial_page_->set_delegate(this);
174  }
175  ~TestInterstitialPageStateGuard() {
176    if (interstitial_page_)
177      interstitial_page_->ClearStates();
178  }
179
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
189class TabContentsTest : public RenderViewHostTestHarness {
190 public:
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.
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
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.
233TEST_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.
245TEST_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.
267TEST_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.
295TEST_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).
348TEST_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.
391TEST_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.
445TEST_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.
497TEST_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.
547TEST_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
580TEST_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).
675TEST_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.
709TEST_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.
745TEST_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.
796TEST_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.
834TEST_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.
852TEST_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.
880TEST_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.
928TEST_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.
973TEST_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.
1017TEST_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.
1076TEST_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.
1131TEST_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.
1176TEST_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.
1197TEST_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.
1230TEST_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.
1268TEST_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.
1294TEST_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.
1314TEST_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.
1348TEST_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.
1398TEST_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.
1453TEST_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.
1482TEST_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.
1520TEST_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).
1553TEST_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.
1596TEST_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.
1629TEST_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.
1671TEST_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
1716class ConstrainedWindowCloseTest : public ConstrainedWindow {
1717 public:
1718  explicit ConstrainedWindowCloseTest(TabContents* tab_contents)
1719      : tab_contents_(tab_contents) {
1720  }
1721
1722  virtual void ShowConstrainedWindow() {}
1723  virtual void FocusConstrainedWindow() {}
1724  virtual ~ConstrainedWindowCloseTest() {}
1725
1726  virtual void CloseConstrainedWindow() {
1727    tab_contents_->WillClose(this);
1728    close_count++;
1729  }
1730
1731  int close_count;
1732  TabContents* tab_contents_;
1733};
1734
1735TEST_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