8ac7161358eb4f900b9bffbed1b448b04d4e71a5
[platform/framework/web/crosswalk.git] / src / content / browser / web_contents / web_contents_impl_unittest.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/logging.h"
6 #include "base/strings/utf_string_conversions.h"
7 #include "content/browser/frame_host/interstitial_page_impl.h"
8 #include "content/browser/frame_host/navigation_entry_impl.h"
9 #include "content/browser/renderer_host/render_view_host_impl.h"
10 #include "content/browser/site_instance_impl.h"
11 #include "content/browser/webui/web_ui_controller_factory_registry.h"
12 #include "content/common/view_messages.h"
13 #include "content/public/browser/global_request_id.h"
14 #include "content/public/browser/interstitial_page_delegate.h"
15 #include "content/public/browser/navigation_details.h"
16 #include "content/public/browser/notification_details.h"
17 #include "content/public/browser/notification_source.h"
18 #include "content/public/browser/render_widget_host_view.h"
19 #include "content/public/browser/web_contents_observer.h"
20 #include "content/public/browser/web_ui_controller.h"
21 #include "content/public/common/bindings_policy.h"
22 #include "content/public/common/content_constants.h"
23 #include "content/public/common/url_constants.h"
24 #include "content/public/common/url_utils.h"
25 #include "content/public/test/mock_render_process_host.h"
26 #include "content/public/test/test_browser_thread.h"
27 #include "content/public/test/test_utils.h"
28 #include "content/test/test_content_browser_client.h"
29 #include "content/test/test_content_client.h"
30 #include "content/test/test_render_view_host.h"
31 #include "content/test/test_web_contents.h"
32 #include "testing/gtest/include/gtest/gtest.h"
33
34 namespace content {
35 namespace {
36
37 const char kTestWebUIUrl[] = "chrome://blah";
38
39 class WebContentsImplTestWebUIControllerFactory
40     : public WebUIControllerFactory {
41  public:
42   virtual WebUIController* CreateWebUIControllerForURL(
43       WebUI* web_ui, const GURL& url) const OVERRIDE {
44     if (!UseWebUI(url))
45       return NULL;
46     return new WebUIController(web_ui);
47   }
48
49   virtual WebUI::TypeID GetWebUIType(BrowserContext* browser_context,
50       const GURL& url) const OVERRIDE {
51     return WebUI::kNoWebUI;
52   }
53
54   virtual bool UseWebUIForURL(BrowserContext* browser_context,
55                               const GURL& url) const OVERRIDE {
56     return UseWebUI(url);
57   }
58
59   virtual bool UseWebUIBindingsForURL(BrowserContext* browser_context,
60                                       const GURL& url) const OVERRIDE {
61     return UseWebUI(url);
62   }
63
64  private:
65   bool UseWebUI(const GURL& url) const {
66     return url == GURL(kTestWebUIUrl);
67   }
68 };
69
70 class TestInterstitialPage;
71
72 class TestInterstitialPageDelegate : public InterstitialPageDelegate {
73  public:
74   explicit TestInterstitialPageDelegate(TestInterstitialPage* interstitial_page)
75       : interstitial_page_(interstitial_page) {}
76   virtual void CommandReceived(const std::string& command) OVERRIDE;
77   virtual std::string GetHTMLContents() OVERRIDE { return std::string(); }
78   virtual void OnDontProceed() OVERRIDE;
79   virtual void OnProceed() OVERRIDE;
80  private:
81   TestInterstitialPage* interstitial_page_;
82 };
83
84 class TestInterstitialPage : public InterstitialPageImpl {
85  public:
86   enum InterstitialState {
87     INVALID = 0,    // Hasn't yet been initialized.
88     UNDECIDED,      // Initialized, but no decision taken yet.
89     OKED,           // Proceed was called.
90     CANCELED        // DontProceed was called.
91   };
92
93   class Delegate {
94    public:
95     virtual void TestInterstitialPageDeleted(
96         TestInterstitialPage* interstitial) = 0;
97
98    protected:
99     virtual ~Delegate() {}
100   };
101
102   // IMPORTANT NOTE: if you pass stack allocated values for |state| and
103   // |deleted| (like all interstitial related tests do at this point), make sure
104   // to create an instance of the TestInterstitialPageStateGuard class on the
105   // stack in your test.  This will ensure that the TestInterstitialPage states
106   // are cleared when the test finishes.
107   // Not doing so will cause stack trashing if your test does not hide the
108   // interstitial, as in such a case it will be destroyed in the test TearDown
109   // method and will dereference the |deleted| local variable which by then is
110   // out of scope.
111   TestInterstitialPage(WebContentsImpl* contents,
112                        bool new_navigation,
113                        const GURL& url,
114                        InterstitialState* state,
115                        bool* deleted)
116       : InterstitialPageImpl(
117             contents,
118             static_cast<RenderWidgetHostDelegate*>(contents),
119             new_navigation, url, new TestInterstitialPageDelegate(this)),
120         state_(state),
121         deleted_(deleted),
122         command_received_count_(0),
123         delegate_(NULL) {
124     *state_ = UNDECIDED;
125     *deleted_ = false;
126   }
127
128   virtual ~TestInterstitialPage() {
129     if (deleted_)
130       *deleted_ = true;
131     if (delegate_)
132       delegate_->TestInterstitialPageDeleted(this);
133   }
134
135   void OnDontProceed() {
136     if (state_)
137       *state_ = CANCELED;
138   }
139   void OnProceed() {
140     if (state_)
141       *state_ = OKED;
142   }
143
144   int command_received_count() const {
145     return command_received_count_;
146   }
147
148   void TestDomOperationResponse(const std::string& json_string) {
149     if (enabled())
150       CommandReceived();
151   }
152
153   void TestDidNavigate(int page_id, const GURL& url) {
154     ViewHostMsg_FrameNavigate_Params params;
155     InitNavigateParams(&params, page_id, url, PAGE_TRANSITION_TYPED);
156     DidNavigate(GetRenderViewHostForTesting(), params);
157   }
158
159   void TestRenderViewTerminated(base::TerminationStatus status,
160                                 int error_code) {
161     RenderViewTerminated(GetRenderViewHostForTesting(), status, error_code);
162   }
163
164   bool is_showing() const {
165     return static_cast<TestRenderWidgetHostView*>(
166         GetRenderViewHostForTesting()->GetView())->is_showing();
167   }
168
169   void ClearStates() {
170     state_ = NULL;
171     deleted_ = NULL;
172     delegate_ = NULL;
173   }
174
175   void CommandReceived() {
176     command_received_count_++;
177   }
178
179   void set_delegate(Delegate* delegate) {
180     delegate_ = delegate;
181   }
182
183  protected:
184   virtual WebContentsView* CreateWebContentsView() OVERRIDE {
185     return NULL;
186   }
187
188  private:
189   InterstitialState* state_;
190   bool* deleted_;
191   int command_received_count_;
192   Delegate* delegate_;
193 };
194
195 void TestInterstitialPageDelegate::CommandReceived(const std::string& command) {
196   interstitial_page_->CommandReceived();
197 }
198
199 void TestInterstitialPageDelegate::OnDontProceed() {
200   interstitial_page_->OnDontProceed();
201 }
202
203 void TestInterstitialPageDelegate::OnProceed() {
204   interstitial_page_->OnProceed();
205 }
206
207 class TestInterstitialPageStateGuard : public TestInterstitialPage::Delegate {
208  public:
209   explicit TestInterstitialPageStateGuard(
210       TestInterstitialPage* interstitial_page)
211       : interstitial_page_(interstitial_page) {
212     DCHECK(interstitial_page_);
213     interstitial_page_->set_delegate(this);
214   }
215   virtual ~TestInterstitialPageStateGuard() {
216     if (interstitial_page_)
217       interstitial_page_->ClearStates();
218   }
219
220   virtual void TestInterstitialPageDeleted(
221       TestInterstitialPage* interstitial) OVERRIDE {
222     DCHECK(interstitial_page_ == interstitial);
223     interstitial_page_ = NULL;
224   }
225
226  private:
227   TestInterstitialPage* interstitial_page_;
228 };
229
230 class WebContentsImplTestBrowserClient : public TestContentBrowserClient {
231  public:
232   WebContentsImplTestBrowserClient()
233       : assign_site_for_url_(false) {}
234
235   virtual ~WebContentsImplTestBrowserClient() {}
236
237   virtual bool ShouldAssignSiteForURL(const GURL& url) OVERRIDE {
238     return assign_site_for_url_;
239   }
240
241   void set_assign_site_for_url(bool assign) {
242     assign_site_for_url_ = assign;
243   }
244
245  private:
246   bool assign_site_for_url_;
247 };
248
249 class WebContentsImplTest : public RenderViewHostImplTestHarness {
250  public:
251   virtual void SetUp() {
252     RenderViewHostImplTestHarness::SetUp();
253     WebUIControllerFactory::RegisterFactory(&factory_);
254   }
255
256   virtual void TearDown() {
257     WebUIControllerFactory::UnregisterFactoryForTesting(&factory_);
258     RenderViewHostImplTestHarness::TearDown();
259   }
260
261  private:
262   WebContentsImplTestWebUIControllerFactory factory_;
263 };
264
265 class TestWebContentsObserver : public WebContentsObserver {
266  public:
267   explicit TestWebContentsObserver(WebContents* contents)
268       : WebContentsObserver(contents) {
269   }
270   virtual ~TestWebContentsObserver() {}
271
272   virtual void DidFinishLoad(int64 frame_id,
273                              const GURL& validated_url,
274                              bool is_main_frame,
275                              RenderViewHost* render_view_host) OVERRIDE {
276     last_url_ = validated_url;
277   }
278   virtual void DidFailLoad(int64 frame_id,
279                            const GURL& validated_url,
280                            bool is_main_frame,
281                            int error_code,
282                            const base::string16& error_description,
283                            RenderViewHost* render_view_host) OVERRIDE {
284     last_url_ = validated_url;
285   }
286
287   const GURL& last_url() const { return last_url_; }
288
289  private:
290   GURL last_url_;
291
292   DISALLOW_COPY_AND_ASSIGN(TestWebContentsObserver);
293 };
294
295 }  // namespace
296
297 // Test to make sure that title updates get stripped of whitespace.
298 TEST_F(WebContentsImplTest, UpdateTitle) {
299   NavigationControllerImpl& cont =
300       static_cast<NavigationControllerImpl&>(controller());
301   ViewHostMsg_FrameNavigate_Params params;
302   InitNavigateParams(&params, 0, GURL(kAboutBlankURL), PAGE_TRANSITION_TYPED);
303
304   LoadCommittedDetails details;
305   cont.RendererDidNavigate(test_rvh(), params, &details);
306
307   contents()->UpdateTitle(rvh(), 0,
308                           base::ASCIIToUTF16("    Lots O' Whitespace\n"),
309                           base::i18n::LEFT_TO_RIGHT);
310   EXPECT_EQ(base::ASCIIToUTF16("Lots O' Whitespace"), contents()->GetTitle());
311 }
312
313 TEST_F(WebContentsImplTest, DontUseTitleFromPendingEntry) {
314   const GURL kGURL("chrome://blah");
315   controller().LoadURL(
316       kGURL, Referrer(), PAGE_TRANSITION_TYPED, std::string());
317   EXPECT_EQ(base::string16(), contents()->GetTitle());
318 }
319
320 TEST_F(WebContentsImplTest, UseTitleFromPendingEntryIfSet) {
321   const GURL kGURL("chrome://blah");
322   const base::string16 title = base::ASCIIToUTF16("My Title");
323   controller().LoadURL(
324       kGURL, Referrer(), PAGE_TRANSITION_TYPED, std::string());
325
326   NavigationEntry* entry = controller().GetVisibleEntry();
327   ASSERT_EQ(kGURL, entry->GetURL());
328   entry->SetTitle(title);
329
330   EXPECT_EQ(title, contents()->GetTitle());
331 }
332
333 // Test view source mode for a webui page.
334 TEST_F(WebContentsImplTest, NTPViewSource) {
335   NavigationControllerImpl& cont =
336       static_cast<NavigationControllerImpl&>(controller());
337   const char kUrl[] = "view-source:chrome://blah";
338   const GURL kGURL(kUrl);
339
340   process()->sink().ClearMessages();
341
342   cont.LoadURL(
343       kGURL, Referrer(), PAGE_TRANSITION_TYPED, std::string());
344   rvh()->GetDelegate()->RenderViewCreated(rvh());
345   // Did we get the expected message?
346   EXPECT_TRUE(process()->sink().GetFirstMessageMatching(
347       ViewMsg_EnableViewSourceMode::ID));
348
349   ViewHostMsg_FrameNavigate_Params params;
350   InitNavigateParams(&params, 0, kGURL, PAGE_TRANSITION_TYPED);
351   LoadCommittedDetails details;
352   cont.RendererDidNavigate(test_rvh(), params, &details);
353   // Also check title and url.
354   EXPECT_EQ(base::ASCIIToUTF16(kUrl), contents()->GetTitle());
355 }
356
357 // Test to ensure UpdateMaxPageID is working properly.
358 TEST_F(WebContentsImplTest, UpdateMaxPageID) {
359   SiteInstance* instance1 = contents()->GetSiteInstance();
360   scoped_refptr<SiteInstance> instance2(SiteInstance::Create(NULL));
361
362   // Starts at -1.
363   EXPECT_EQ(-1, contents()->GetMaxPageID());
364   EXPECT_EQ(-1, contents()->GetMaxPageIDForSiteInstance(instance1));
365   EXPECT_EQ(-1, contents()->GetMaxPageIDForSiteInstance(instance2.get()));
366
367   // Make sure max_page_id_ is monotonically increasing per SiteInstance.
368   contents()->UpdateMaxPageID(3);
369   contents()->UpdateMaxPageID(1);
370   EXPECT_EQ(3, contents()->GetMaxPageID());
371   EXPECT_EQ(3, contents()->GetMaxPageIDForSiteInstance(instance1));
372   EXPECT_EQ(-1, contents()->GetMaxPageIDForSiteInstance(instance2.get()));
373
374   contents()->UpdateMaxPageIDForSiteInstance(instance2.get(), 7);
375   EXPECT_EQ(3, contents()->GetMaxPageID());
376   EXPECT_EQ(3, contents()->GetMaxPageIDForSiteInstance(instance1));
377   EXPECT_EQ(7, contents()->GetMaxPageIDForSiteInstance(instance2.get()));
378 }
379
380 // Test simple same-SiteInstance navigation.
381 TEST_F(WebContentsImplTest, SimpleNavigation) {
382   TestRenderViewHost* orig_rvh = test_rvh();
383   SiteInstance* instance1 = contents()->GetSiteInstance();
384   EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
385
386   // Navigate to URL
387   const GURL url("http://www.google.com");
388   controller().LoadURL(
389       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
390   EXPECT_FALSE(contents()->cross_navigation_pending());
391   EXPECT_EQ(instance1, orig_rvh->GetSiteInstance());
392   // Controller's pending entry will have a NULL site instance until we assign
393   // it in DidNavigate.
394   EXPECT_TRUE(
395       NavigationEntryImpl::FromNavigationEntry(controller().GetVisibleEntry())->
396           site_instance() == NULL);
397
398   // DidNavigate from the page
399   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
400   EXPECT_FALSE(contents()->cross_navigation_pending());
401   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
402   EXPECT_EQ(instance1, orig_rvh->GetSiteInstance());
403   // Controller's entry should now have the SiteInstance, or else we won't be
404   // able to find it later.
405   EXPECT_EQ(
406       instance1,
407       NavigationEntryImpl::FromNavigationEntry(controller().GetVisibleEntry())->
408           site_instance());
409 }
410
411 // Test that we reject NavigateToEntry if the url is over kMaxURLChars.
412 TEST_F(WebContentsImplTest, NavigateToExcessivelyLongURL) {
413   // Construct a URL that's kMaxURLChars + 1 long of all 'a's.
414   const GURL url(std::string("http://example.org/").append(
415       GetMaxURLChars() + 1, 'a'));
416
417   controller().LoadURL(
418       url, Referrer(), PAGE_TRANSITION_GENERATED, std::string());
419   EXPECT_TRUE(controller().GetVisibleEntry() == NULL);
420 }
421
422 // Test that navigating across a site boundary creates a new RenderViewHost
423 // with a new SiteInstance.  Going back should do the same.
424 TEST_F(WebContentsImplTest, CrossSiteBoundaries) {
425   contents()->transition_cross_site = true;
426   TestRenderViewHost* orig_rvh = test_rvh();
427   RenderFrameHostImpl* orig_rfh =
428       contents()->GetFrameTree()->root()->current_frame_host();
429   int orig_rvh_delete_count = 0;
430   orig_rvh->set_delete_counter(&orig_rvh_delete_count);
431   SiteInstance* instance1 = contents()->GetSiteInstance();
432
433   // Navigate to URL.  First URL should use first RenderViewHost.
434   const GURL url("http://www.google.com");
435   controller().LoadURL(
436       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
437   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
438
439   // Keep the number of active views in orig_rvh's SiteInstance
440   // non-zero so that orig_rvh doesn't get deleted when it gets
441   // swapped out.
442   static_cast<SiteInstanceImpl*>(orig_rvh->GetSiteInstance())->
443       increment_active_view_count();
444
445   EXPECT_FALSE(contents()->cross_navigation_pending());
446   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
447   EXPECT_EQ(url, contents()->GetLastCommittedURL());
448   EXPECT_EQ(url, contents()->GetVisibleURL());
449
450   // Navigate to new site
451   const GURL url2("http://www.yahoo.com");
452   controller().LoadURL(
453       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
454   EXPECT_TRUE(contents()->cross_navigation_pending());
455   EXPECT_EQ(url, contents()->GetLastCommittedURL());
456   EXPECT_EQ(url2, contents()->GetVisibleURL());
457   TestRenderViewHost* pending_rvh =
458       static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
459   int pending_rvh_delete_count = 0;
460   pending_rvh->set_delete_counter(&pending_rvh_delete_count);
461   RenderFrameHostImpl* pending_rfh = contents()->GetFrameTree()->root()->
462       render_manager()->pending_frame_host();
463
464   // Navigations should be suspended in pending_rvh until ShouldCloseACK.
465   EXPECT_TRUE(pending_rvh->are_navigations_suspended());
466   orig_rvh->SendShouldCloseACK(true);
467   EXPECT_FALSE(pending_rvh->are_navigations_suspended());
468
469   // DidNavigate from the pending page
470   contents()->TestDidNavigate(
471       pending_rvh, 1, url2, PAGE_TRANSITION_TYPED);
472   SiteInstance* instance2 = contents()->GetSiteInstance();
473
474   // Keep the number of active views in pending_rvh's SiteInstance
475   // non-zero so that orig_rvh doesn't get deleted when it gets
476   // swapped out.
477   static_cast<SiteInstanceImpl*>(pending_rvh->GetSiteInstance())->
478       increment_active_view_count();
479
480   EXPECT_FALSE(contents()->cross_navigation_pending());
481   EXPECT_EQ(pending_rvh, contents()->GetRenderViewHost());
482   EXPECT_EQ(url2, contents()->GetLastCommittedURL());
483   EXPECT_EQ(url2, contents()->GetVisibleURL());
484   EXPECT_NE(instance1, instance2);
485   EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
486   // We keep the original RFH around, swapped out.
487   EXPECT_TRUE(contents()->GetRenderManagerForTesting()->IsOnSwappedOutList(
488       orig_rfh));
489   EXPECT_EQ(orig_rvh_delete_count, 0);
490
491   // Going back should switch SiteInstances again.  The first SiteInstance is
492   // stored in the NavigationEntry, so it should be the same as at the start.
493   // We should use the same RVH as before, swapping it back in.
494   controller().GoBack();
495   TestRenderViewHost* goback_rvh =
496       static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
497   EXPECT_EQ(orig_rvh, goback_rvh);
498   EXPECT_TRUE(contents()->cross_navigation_pending());
499
500   // Navigations should be suspended in goback_rvh until ShouldCloseACK.
501   EXPECT_TRUE(goback_rvh->are_navigations_suspended());
502   pending_rvh->SendShouldCloseACK(true);
503   EXPECT_FALSE(goback_rvh->are_navigations_suspended());
504
505   // DidNavigate from the back action
506   contents()->TestDidNavigate(
507       goback_rvh, 1, url2, PAGE_TRANSITION_TYPED);
508   EXPECT_FALSE(contents()->cross_navigation_pending());
509   EXPECT_EQ(goback_rvh, contents()->GetRenderViewHost());
510   EXPECT_EQ(instance1, contents()->GetSiteInstance());
511   // The pending RFH should now be swapped out, not deleted.
512   EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
513       IsOnSwappedOutList(pending_rfh));
514   EXPECT_EQ(pending_rvh_delete_count, 0);
515
516   // Close contents and ensure RVHs are deleted.
517   DeleteContents();
518   EXPECT_EQ(orig_rvh_delete_count, 1);
519   EXPECT_EQ(pending_rvh_delete_count, 1);
520 }
521
522 // Test that navigating across a site boundary after a crash creates a new
523 // RVH without requiring a cross-site transition (i.e., PENDING state).
524 TEST_F(WebContentsImplTest, CrossSiteBoundariesAfterCrash) {
525   contents()->transition_cross_site = true;
526   TestRenderViewHost* orig_rvh = test_rvh();
527   int orig_rvh_delete_count = 0;
528   orig_rvh->set_delete_counter(&orig_rvh_delete_count);
529   SiteInstance* instance1 = contents()->GetSiteInstance();
530
531   // Navigate to URL.  First URL should use first RenderViewHost.
532   const GURL url("http://www.google.com");
533   controller().LoadURL(
534       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
535   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
536
537   EXPECT_FALSE(contents()->cross_navigation_pending());
538   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
539
540   // Crash the renderer.
541   orig_rvh->set_render_view_created(false);
542
543   // Navigate to new site.  We should not go into PENDING.
544   const GURL url2("http://www.yahoo.com");
545   controller().LoadURL(
546       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
547   RenderViewHost* new_rvh = rvh();
548   EXPECT_FALSE(contents()->cross_navigation_pending());
549   EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
550   EXPECT_NE(orig_rvh, new_rvh);
551   EXPECT_EQ(orig_rvh_delete_count, 1);
552
553   // DidNavigate from the new page
554   contents()->TestDidNavigate(new_rvh, 1, url2, PAGE_TRANSITION_TYPED);
555   SiteInstance* instance2 = contents()->GetSiteInstance();
556
557   EXPECT_FALSE(contents()->cross_navigation_pending());
558   EXPECT_EQ(new_rvh, rvh());
559   EXPECT_NE(instance1, instance2);
560   EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
561
562   // Close contents and ensure RVHs are deleted.
563   DeleteContents();
564   EXPECT_EQ(orig_rvh_delete_count, 1);
565 }
566
567 // Test that opening a new contents in the same SiteInstance and then navigating
568 // both contentses to a new site will place both contentses in a single
569 // SiteInstance.
570 TEST_F(WebContentsImplTest, NavigateTwoTabsCrossSite) {
571   contents()->transition_cross_site = true;
572   TestRenderViewHost* orig_rvh = test_rvh();
573   SiteInstance* instance1 = contents()->GetSiteInstance();
574
575   // Navigate to URL.  First URL should use first RenderViewHost.
576   const GURL url("http://www.google.com");
577   controller().LoadURL(
578       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
579   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
580
581   // Open a new contents with the same SiteInstance, navigated to the same site.
582   scoped_ptr<TestWebContents> contents2(
583       TestWebContents::Create(browser_context(), instance1));
584   contents2->transition_cross_site = true;
585   contents2->GetController().LoadURL(url, Referrer(),
586                                     PAGE_TRANSITION_TYPED,
587                                     std::string());
588   // Need this page id to be 2 since the site instance is the same (which is the
589   // scope of page IDs) and we want to consider this a new page.
590   contents2->TestDidNavigate(
591       contents2->GetRenderViewHost(), 2, url, PAGE_TRANSITION_TYPED);
592
593   // Navigate first contents to a new site.
594   const GURL url2a("http://www.yahoo.com");
595   controller().LoadURL(
596       url2a, Referrer(), PAGE_TRANSITION_TYPED, std::string());
597   orig_rvh->SendShouldCloseACK(true);
598   TestRenderViewHost* pending_rvh_a =
599       static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
600   contents()->TestDidNavigate(
601       pending_rvh_a, 1, url2a, PAGE_TRANSITION_TYPED);
602   SiteInstance* instance2a = contents()->GetSiteInstance();
603   EXPECT_NE(instance1, instance2a);
604
605   // Navigate second contents to the same site as the first tab.
606   const GURL url2b("http://mail.yahoo.com");
607   contents2->GetController().LoadURL(url2b, Referrer(),
608                                     PAGE_TRANSITION_TYPED,
609                                     std::string());
610   TestRenderViewHost* rvh2 =
611       static_cast<TestRenderViewHost*>(contents2->GetRenderViewHost());
612   rvh2->SendShouldCloseACK(true);
613   TestRenderViewHost* pending_rvh_b =
614       static_cast<TestRenderViewHost*>(contents2->GetPendingRenderViewHost());
615   EXPECT_TRUE(pending_rvh_b != NULL);
616   EXPECT_TRUE(contents2->cross_navigation_pending());
617
618   // NOTE(creis): We used to be in danger of showing a crash page here if the
619   // second contents hadn't navigated somewhere first (bug 1145430).  That case
620   // is now covered by the CrossSiteBoundariesAfterCrash test.
621   contents2->TestDidNavigate(
622       pending_rvh_b, 2, url2b, PAGE_TRANSITION_TYPED);
623   SiteInstance* instance2b = contents2->GetSiteInstance();
624   EXPECT_NE(instance1, instance2b);
625
626   // Both contentses should now be in the same SiteInstance.
627   EXPECT_EQ(instance2a, instance2b);
628 }
629
630 TEST_F(WebContentsImplTest, NavigateDoesNotUseUpSiteInstance) {
631   WebContentsImplTestBrowserClient browser_client;
632   SetBrowserClientForTesting(&browser_client);
633
634   contents()->transition_cross_site = true;
635   TestRenderViewHost* orig_rvh = test_rvh();
636   RenderFrameHostImpl* orig_rfh =
637       contents()->GetFrameTree()->root()->current_frame_host();
638   int orig_rvh_delete_count = 0;
639   orig_rvh->set_delete_counter(&orig_rvh_delete_count);
640   SiteInstanceImpl* orig_instance =
641       static_cast<SiteInstanceImpl*>(contents()->GetSiteInstance());
642
643   browser_client.set_assign_site_for_url(false);
644   // Navigate to an URL that will not assign a new SiteInstance.
645   const GURL native_url("non-site-url://stuffandthings");
646   controller().LoadURL(
647       native_url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
648   contents()->TestDidNavigate(orig_rvh, 1, native_url, PAGE_TRANSITION_TYPED);
649
650   EXPECT_FALSE(contents()->cross_navigation_pending());
651   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
652   EXPECT_EQ(native_url, contents()->GetLastCommittedURL());
653   EXPECT_EQ(native_url, contents()->GetVisibleURL());
654   EXPECT_EQ(orig_instance, contents()->GetSiteInstance());
655   EXPECT_EQ(GURL(), contents()->GetSiteInstance()->GetSiteURL());
656   EXPECT_FALSE(orig_instance->HasSite());
657
658   browser_client.set_assign_site_for_url(true);
659   // Navigate to new site (should keep same site instance).
660   const GURL url("http://www.google.com");
661   controller().LoadURL(
662       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
663   EXPECT_FALSE(contents()->cross_navigation_pending());
664   EXPECT_EQ(native_url, contents()->GetLastCommittedURL());
665   EXPECT_EQ(url, contents()->GetVisibleURL());
666   EXPECT_FALSE(contents()->GetPendingRenderViewHost());
667   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
668
669   // Keep the number of active views in orig_rvh's SiteInstance
670   // non-zero so that orig_rvh doesn't get deleted when it gets
671   // swapped out.
672   static_cast<SiteInstanceImpl*>(orig_rvh->GetSiteInstance())->
673       increment_active_view_count();
674
675   EXPECT_EQ(orig_instance, contents()->GetSiteInstance());
676   EXPECT_TRUE(
677       contents()->GetSiteInstance()->GetSiteURL().DomainIs("google.com"));
678   EXPECT_EQ(url, contents()->GetLastCommittedURL());
679
680   // Navigate to another new site (should create a new site instance).
681   const GURL url2("http://www.yahoo.com");
682   controller().LoadURL(
683       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
684   EXPECT_TRUE(contents()->cross_navigation_pending());
685   EXPECT_EQ(url, contents()->GetLastCommittedURL());
686   EXPECT_EQ(url2, contents()->GetVisibleURL());
687   TestRenderViewHost* pending_rvh =
688       static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
689   int pending_rvh_delete_count = 0;
690   pending_rvh->set_delete_counter(&pending_rvh_delete_count);
691
692   // Navigations should be suspended in pending_rvh until ShouldCloseACK.
693   EXPECT_TRUE(pending_rvh->are_navigations_suspended());
694   orig_rvh->SendShouldCloseACK(true);
695   EXPECT_FALSE(pending_rvh->are_navigations_suspended());
696
697   // DidNavigate from the pending page.
698   contents()->TestDidNavigate(
699       pending_rvh, 1, url2, PAGE_TRANSITION_TYPED);
700   SiteInstance* new_instance = contents()->GetSiteInstance();
701
702   EXPECT_FALSE(contents()->cross_navigation_pending());
703   EXPECT_EQ(pending_rvh, contents()->GetRenderViewHost());
704   EXPECT_EQ(url2, contents()->GetLastCommittedURL());
705   EXPECT_EQ(url2, contents()->GetVisibleURL());
706   EXPECT_NE(new_instance, orig_instance);
707   EXPECT_FALSE(contents()->GetPendingRenderViewHost());
708   // We keep the original RFH around, swapped out.
709   EXPECT_TRUE(contents()->GetRenderManagerForTesting()->IsOnSwappedOutList(
710       orig_rfh));
711   EXPECT_EQ(orig_rvh_delete_count, 0);
712
713   // Close contents and ensure RVHs are deleted.
714   DeleteContents();
715   EXPECT_EQ(orig_rvh_delete_count, 1);
716   EXPECT_EQ(pending_rvh_delete_count, 1);
717 }
718
719 // Test that we can find an opener RVH even if it's pending.
720 // http://crbug.com/176252.
721 TEST_F(WebContentsImplTest, FindOpenerRVHWhenPending) {
722   contents()->transition_cross_site = true;
723   TestRenderViewHost* orig_rvh = test_rvh();
724
725   // Navigate to a URL.
726   const GURL url("http://www.google.com");
727   controller().LoadURL(
728       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
729   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
730
731   // Start to navigate first tab to a new site, so that it has a pending RVH.
732   const GURL url2("http://www.yahoo.com");
733   controller().LoadURL(
734       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
735   orig_rvh->SendShouldCloseACK(true);
736   TestRenderViewHost* pending_rvh =
737       static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
738
739   // While it is still pending, simulate opening a new tab with the first tab
740   // as its opener.  This will call WebContentsImpl::CreateOpenerRenderViews
741   // on the opener to ensure that an RVH exists.
742   int opener_routing_id = contents()->CreateOpenerRenderViews(
743       pending_rvh->GetSiteInstance());
744
745   // We should find the pending RVH and not create a new one.
746   EXPECT_EQ(pending_rvh->GetRoutingID(), opener_routing_id);
747 }
748
749 // Tests that WebContentsImpl uses the current URL, not the SiteInstance's site,
750 // to determine whether a navigation is cross-site.
751 TEST_F(WebContentsImplTest, CrossSiteComparesAgainstCurrentPage) {
752   contents()->transition_cross_site = true;
753   RenderViewHost* orig_rvh = rvh();
754   SiteInstance* instance1 = contents()->GetSiteInstance();
755
756   // Navigate to URL.
757   const GURL url("http://www.google.com");
758   controller().LoadURL(
759       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
760   contents()->TestDidNavigate(
761       orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
762
763   // Open a related contents to a second site.
764   scoped_ptr<TestWebContents> contents2(
765       TestWebContents::Create(browser_context(), instance1));
766   contents2->transition_cross_site = true;
767   const GURL url2("http://www.yahoo.com");
768   contents2->GetController().LoadURL(url2, Referrer(),
769                                     PAGE_TRANSITION_TYPED,
770                                     std::string());
771   // The first RVH in contents2 isn't live yet, so we shortcut the cross site
772   // pending.
773   TestRenderViewHost* rvh2 = static_cast<TestRenderViewHost*>(
774       contents2->GetRenderViewHost());
775   EXPECT_FALSE(contents2->cross_navigation_pending());
776   contents2->TestDidNavigate(rvh2, 2, url2, PAGE_TRANSITION_TYPED);
777   SiteInstance* instance2 = contents2->GetSiteInstance();
778   EXPECT_NE(instance1, instance2);
779   EXPECT_FALSE(contents2->cross_navigation_pending());
780
781   // Simulate a link click in first contents to second site.  Doesn't switch
782   // SiteInstances, because we don't intercept WebKit navigations.
783   contents()->TestDidNavigate(
784       orig_rvh, 2, url2, PAGE_TRANSITION_TYPED);
785   SiteInstance* instance3 = contents()->GetSiteInstance();
786   EXPECT_EQ(instance1, instance3);
787   EXPECT_FALSE(contents()->cross_navigation_pending());
788
789   // Navigate to the new site.  Doesn't switch SiteInstancees, because we
790   // compare against the current URL, not the SiteInstance's site.
791   const GURL url3("http://mail.yahoo.com");
792   controller().LoadURL(
793       url3, Referrer(), PAGE_TRANSITION_TYPED, std::string());
794   EXPECT_FALSE(contents()->cross_navigation_pending());
795   contents()->TestDidNavigate(
796       orig_rvh, 3, url3, PAGE_TRANSITION_TYPED);
797   SiteInstance* instance4 = contents()->GetSiteInstance();
798   EXPECT_EQ(instance1, instance4);
799 }
800
801 // Test that the onbeforeunload and onunload handlers run when navigating
802 // across site boundaries.
803 TEST_F(WebContentsImplTest, CrossSiteUnloadHandlers) {
804   contents()->transition_cross_site = true;
805   TestRenderViewHost* orig_rvh = test_rvh();
806   SiteInstance* instance1 = contents()->GetSiteInstance();
807
808   // Navigate to URL.  First URL should use first RenderViewHost.
809   const GURL url("http://www.google.com");
810   controller().LoadURL(
811       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
812   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
813   EXPECT_FALSE(contents()->cross_navigation_pending());
814   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
815
816   // Navigate to new site, but simulate an onbeforeunload denial.
817   const GURL url2("http://www.yahoo.com");
818   controller().LoadURL(
819       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
820   EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
821   base::TimeTicks now = base::TimeTicks::Now();
822   orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, false, now, now));
823   EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
824   EXPECT_FALSE(contents()->cross_navigation_pending());
825   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
826
827   // Navigate again, but simulate an onbeforeunload approval.
828   controller().LoadURL(
829       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
830   EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
831   now = base::TimeTicks::Now();
832   orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
833   EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
834   EXPECT_TRUE(contents()->cross_navigation_pending());
835   TestRenderViewHost* pending_rvh = static_cast<TestRenderViewHost*>(
836       contents()->GetPendingRenderViewHost());
837
838   // We won't hear DidNavigate until the onunload handler has finished running.
839
840   // DidNavigate from the pending page.
841   contents()->TestDidNavigate(
842       pending_rvh, 1, url2, PAGE_TRANSITION_TYPED);
843   SiteInstance* instance2 = contents()->GetSiteInstance();
844   EXPECT_FALSE(contents()->cross_navigation_pending());
845   EXPECT_EQ(pending_rvh, rvh());
846   EXPECT_NE(instance1, instance2);
847   EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
848 }
849
850 // Test that during a slow cross-site navigation, the original renderer can
851 // navigate to a different URL and have it displayed, canceling the slow
852 // navigation.
853 TEST_F(WebContentsImplTest, CrossSiteNavigationPreempted) {
854   contents()->transition_cross_site = true;
855   TestRenderViewHost* orig_rvh = test_rvh();
856   SiteInstance* instance1 = contents()->GetSiteInstance();
857
858   // Navigate to URL.  First URL should use first RenderViewHost.
859   const GURL url("http://www.google.com");
860   controller().LoadURL(
861       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
862   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
863   EXPECT_FALSE(contents()->cross_navigation_pending());
864   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
865
866   // Navigate to new site, simulating an onbeforeunload approval.
867   const GURL url2("http://www.yahoo.com");
868   controller().LoadURL(
869       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
870   EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
871   base::TimeTicks now = base::TimeTicks::Now();
872   orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
873   EXPECT_TRUE(contents()->cross_navigation_pending());
874
875   // Suppose the original renderer navigates before the new one is ready.
876   orig_rvh->SendNavigate(2, GURL("http://www.google.com/foo"));
877
878   // Verify that the pending navigation is cancelled.
879   EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
880   SiteInstance* instance2 = contents()->GetSiteInstance();
881   EXPECT_FALSE(contents()->cross_navigation_pending());
882   EXPECT_EQ(orig_rvh, rvh());
883   EXPECT_EQ(instance1, instance2);
884   EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
885 }
886
887 TEST_F(WebContentsImplTest, CrossSiteNavigationBackPreempted) {
888   contents()->transition_cross_site = true;
889
890   // Start with a web ui page, which gets a new RVH with WebUI bindings.
891   const GURL url1("chrome://blah");
892   controller().LoadURL(
893       url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
894   TestRenderViewHost* ntp_rvh = test_rvh();
895   contents()->TestDidNavigate(ntp_rvh, 1, url1, PAGE_TRANSITION_TYPED);
896   NavigationEntry* entry1 = controller().GetLastCommittedEntry();
897   SiteInstance* instance1 = contents()->GetSiteInstance();
898
899   EXPECT_FALSE(contents()->cross_navigation_pending());
900   EXPECT_EQ(ntp_rvh, contents()->GetRenderViewHost());
901   EXPECT_EQ(url1, entry1->GetURL());
902   EXPECT_EQ(instance1,
903             NavigationEntryImpl::FromNavigationEntry(entry1)->site_instance());
904   EXPECT_TRUE(ntp_rvh->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
905
906   // Navigate to new site.
907   const GURL url2("http://www.google.com");
908   controller().LoadURL(
909       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
910   EXPECT_TRUE(contents()->cross_navigation_pending());
911   TestRenderViewHost* google_rvh =
912       static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
913
914   // Simulate beforeunload approval.
915   EXPECT_TRUE(ntp_rvh->is_waiting_for_beforeunload_ack());
916   base::TimeTicks now = base::TimeTicks::Now();
917   ntp_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
918
919   // DidNavigate from the pending page.
920   contents()->TestDidNavigate(
921       google_rvh, 1, url2, PAGE_TRANSITION_TYPED);
922   NavigationEntry* entry2 = controller().GetLastCommittedEntry();
923   SiteInstance* instance2 = contents()->GetSiteInstance();
924
925   EXPECT_FALSE(contents()->cross_navigation_pending());
926   EXPECT_EQ(google_rvh, contents()->GetRenderViewHost());
927   EXPECT_NE(instance1, instance2);
928   EXPECT_FALSE(contents()->GetPendingRenderViewHost());
929   EXPECT_EQ(url2, entry2->GetURL());
930   EXPECT_EQ(instance2,
931             NavigationEntryImpl::FromNavigationEntry(entry2)->site_instance());
932   EXPECT_FALSE(google_rvh->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
933
934   // Navigate to third page on same site.
935   const GURL url3("http://news.google.com");
936   controller().LoadURL(
937       url3, Referrer(), PAGE_TRANSITION_TYPED, std::string());
938   EXPECT_FALSE(contents()->cross_navigation_pending());
939   contents()->TestDidNavigate(
940       google_rvh, 2, url3, PAGE_TRANSITION_TYPED);
941   NavigationEntry* entry3 = controller().GetLastCommittedEntry();
942   SiteInstance* instance3 = contents()->GetSiteInstance();
943
944   EXPECT_FALSE(contents()->cross_navigation_pending());
945   EXPECT_EQ(google_rvh, contents()->GetRenderViewHost());
946   EXPECT_EQ(instance2, instance3);
947   EXPECT_FALSE(contents()->GetPendingRenderViewHost());
948   EXPECT_EQ(url3, entry3->GetURL());
949   EXPECT_EQ(instance3,
950             NavigationEntryImpl::FromNavigationEntry(entry3)->site_instance());
951
952   // Go back within the site.
953   controller().GoBack();
954   EXPECT_FALSE(contents()->cross_navigation_pending());
955   EXPECT_EQ(entry2, controller().GetPendingEntry());
956
957   // Before that commits, go back again.
958   controller().GoBack();
959   EXPECT_TRUE(contents()->cross_navigation_pending());
960   EXPECT_TRUE(contents()->GetPendingRenderViewHost());
961   EXPECT_EQ(entry1, controller().GetPendingEntry());
962
963   // Simulate beforeunload approval.
964   EXPECT_TRUE(google_rvh->is_waiting_for_beforeunload_ack());
965   now = base::TimeTicks::Now();
966   google_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
967
968   // DidNavigate from the first back. This aborts the second back's pending RVH.
969   contents()->TestDidNavigate(google_rvh, 1, url2, PAGE_TRANSITION_TYPED);
970
971   // We should commit this page and forget about the second back.
972   EXPECT_FALSE(contents()->cross_navigation_pending());
973   EXPECT_FALSE(controller().GetPendingEntry());
974   EXPECT_EQ(google_rvh, contents()->GetRenderViewHost());
975   EXPECT_EQ(url2, controller().GetLastCommittedEntry()->GetURL());
976
977   // We should not have corrupted the NTP entry.
978   EXPECT_EQ(instance3,
979             NavigationEntryImpl::FromNavigationEntry(entry3)->site_instance());
980   EXPECT_EQ(instance2,
981             NavigationEntryImpl::FromNavigationEntry(entry2)->site_instance());
982   EXPECT_EQ(instance1,
983             NavigationEntryImpl::FromNavigationEntry(entry1)->site_instance());
984   EXPECT_EQ(url1, entry1->GetURL());
985 }
986
987 // Test that during a slow cross-site navigation, a sub-frame navigation in the
988 // original renderer will not cancel the slow navigation (bug 42029).
989 TEST_F(WebContentsImplTest, CrossSiteNavigationNotPreemptedByFrame) {
990   contents()->transition_cross_site = true;
991   TestRenderViewHost* orig_rvh = test_rvh();
992
993   // Navigate to URL.  First URL should use first RenderViewHost.
994   const GURL url("http://www.google.com");
995   controller().LoadURL(
996       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
997   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
998   EXPECT_FALSE(contents()->cross_navigation_pending());
999   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
1000
1001   // Start navigating to new site.
1002   const GURL url2("http://www.yahoo.com");
1003   controller().LoadURL(
1004       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1005
1006   // Simulate a sub-frame navigation arriving and ensure the RVH is still
1007   // waiting for a before unload response.
1008   orig_rvh->SendNavigateWithTransition(1, GURL("http://google.com/frame"),
1009                                        PAGE_TRANSITION_AUTO_SUBFRAME);
1010   EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
1011
1012   // Now simulate the onbeforeunload approval and verify the navigation is
1013   // not canceled.
1014   base::TimeTicks now = base::TimeTicks::Now();
1015   orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
1016   EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
1017   EXPECT_TRUE(contents()->cross_navigation_pending());
1018 }
1019
1020 // Test that a cross-site navigation is not preempted if the previous
1021 // renderer sends a FrameNavigate message just before being told to stop.
1022 // We should only preempt the cross-site navigation if the previous renderer
1023 // has started a new navigation.  See http://crbug.com/79176.
1024 TEST_F(WebContentsImplTest, CrossSiteNotPreemptedDuringBeforeUnload) {
1025   contents()->transition_cross_site = true;
1026
1027   // Navigate to NTP URL.
1028   const GURL url("chrome://blah");
1029   controller().LoadURL(
1030       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1031   TestRenderViewHost* orig_rvh = test_rvh();
1032   EXPECT_FALSE(contents()->cross_navigation_pending());
1033
1034   // Navigate to new site, with the beforeunload request in flight.
1035   const GURL url2("http://www.yahoo.com");
1036   controller().LoadURL(
1037       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1038   TestRenderViewHost* pending_rvh =
1039       static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
1040   EXPECT_TRUE(contents()->cross_navigation_pending());
1041   EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
1042
1043   // Suppose the first navigation tries to commit now, with a
1044   // ViewMsg_Stop in flight.  This should not cancel the pending navigation,
1045   // but it should act as if the beforeunload ack arrived.
1046   orig_rvh->SendNavigate(1, GURL("chrome://blah"));
1047   EXPECT_TRUE(contents()->cross_navigation_pending());
1048   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
1049   EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
1050
1051   // The pending navigation should be able to commit successfully.
1052   contents()->TestDidNavigate(pending_rvh, 1, url2, PAGE_TRANSITION_TYPED);
1053   EXPECT_FALSE(contents()->cross_navigation_pending());
1054   EXPECT_EQ(pending_rvh, contents()->GetRenderViewHost());
1055 }
1056
1057 // Test that the original renderer cannot preempt a cross-site navigation once
1058 // the unload request has been made.  At this point, the cross-site navigation
1059 // is almost ready to be displayed, and the original renderer is only given a
1060 // short chance to run an unload handler.  Prevents regression of bug 23942.
1061 TEST_F(WebContentsImplTest, CrossSiteCantPreemptAfterUnload) {
1062   contents()->transition_cross_site = true;
1063   TestRenderViewHost* orig_rvh = test_rvh();
1064   SiteInstance* instance1 = contents()->GetSiteInstance();
1065
1066   // Navigate to URL.  First URL should use first RenderViewHost.
1067   const GURL url("http://www.google.com");
1068   controller().LoadURL(
1069       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1070   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
1071   EXPECT_FALSE(contents()->cross_navigation_pending());
1072   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
1073
1074   // Navigate to new site, simulating an onbeforeunload approval.
1075   const GURL url2("http://www.yahoo.com");
1076   controller().LoadURL(
1077       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1078   base::TimeTicks now = base::TimeTicks::Now();
1079   orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
1080   EXPECT_TRUE(contents()->cross_navigation_pending());
1081   TestRenderViewHost* pending_rvh = static_cast<TestRenderViewHost*>(
1082       contents()->GetPendingRenderViewHost());
1083
1084   // Simulate the pending renderer's response, which leads to an unload request
1085   // being sent to orig_rvh.
1086   std::vector<GURL> url_chain;
1087   url_chain.push_back(GURL());
1088   contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
1089       pending_rvh, GlobalRequestID(0, 0), false, url_chain, Referrer(),
1090       PAGE_TRANSITION_TYPED, 1, false);
1091
1092   // Suppose the original renderer navigates now, while the unload request is in
1093   // flight.  We should ignore it, wait for the unload ack, and let the pending
1094   // request continue.  Otherwise, the contents may close spontaneously or stop
1095   // responding to navigation requests.  (See bug 23942.)
1096   ViewHostMsg_FrameNavigate_Params params1a;
1097   InitNavigateParams(&params1a, 2, GURL("http://www.google.com/foo"),
1098                      PAGE_TRANSITION_TYPED);
1099   orig_rvh->SendNavigate(2, GURL("http://www.google.com/foo"));
1100
1101   // Verify that the pending navigation is still in progress.
1102   EXPECT_TRUE(contents()->cross_navigation_pending());
1103   EXPECT_TRUE(contents()->GetPendingRenderViewHost() != NULL);
1104
1105   // DidNavigate from the pending page should commit it.
1106   contents()->TestDidNavigate(
1107       pending_rvh, 1, url2, PAGE_TRANSITION_TYPED);
1108   SiteInstance* instance2 = contents()->GetSiteInstance();
1109   EXPECT_FALSE(contents()->cross_navigation_pending());
1110   EXPECT_EQ(pending_rvh, rvh());
1111   EXPECT_NE(instance1, instance2);
1112   EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
1113 }
1114
1115 // Test that a cross-site navigation that doesn't commit after the unload
1116 // handler doesn't leave the contents in a stuck state.  http://crbug.com/88562
1117 TEST_F(WebContentsImplTest, CrossSiteNavigationCanceled) {
1118   contents()->transition_cross_site = true;
1119   TestRenderViewHost* orig_rvh = test_rvh();
1120   SiteInstance* instance1 = contents()->GetSiteInstance();
1121
1122   // Navigate to URL.  First URL should use first RenderViewHost.
1123   const GURL url("http://www.google.com");
1124   controller().LoadURL(
1125       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1126   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
1127   EXPECT_FALSE(contents()->cross_navigation_pending());
1128   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
1129
1130   // Navigate to new site, simulating an onbeforeunload approval.
1131   const GURL url2("http://www.yahoo.com");
1132   controller().LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1133   EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
1134   base::TimeTicks now = base::TimeTicks::Now();
1135   orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
1136   EXPECT_TRUE(contents()->cross_navigation_pending());
1137
1138   // Simulate swap out message when the response arrives.
1139   orig_rvh->set_is_swapped_out(true);
1140
1141   // Suppose the navigation doesn't get a chance to commit, and the user
1142   // navigates in the current RVH's SiteInstance.
1143   controller().LoadURL(url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1144
1145   // Verify that the pending navigation is cancelled and the renderer is no
1146   // longer swapped out.
1147   EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
1148   SiteInstance* instance2 = contents()->GetSiteInstance();
1149   EXPECT_FALSE(contents()->cross_navigation_pending());
1150   EXPECT_EQ(orig_rvh, rvh());
1151   EXPECT_FALSE(orig_rvh->is_swapped_out());
1152   EXPECT_EQ(instance1, instance2);
1153   EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
1154 }
1155
1156 // Test that NavigationEntries have the correct page state after going
1157 // forward and back.  Prevents regression for bug 1116137.
1158 TEST_F(WebContentsImplTest, NavigationEntryContentState) {
1159   TestRenderViewHost* orig_rvh = test_rvh();
1160
1161   // Navigate to URL.  There should be no committed entry yet.
1162   const GURL url("http://www.google.com");
1163   controller().LoadURL(url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1164   NavigationEntry* entry = controller().GetLastCommittedEntry();
1165   EXPECT_TRUE(entry == NULL);
1166
1167   // Committed entry should have page state after DidNavigate.
1168   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
1169   entry = controller().GetLastCommittedEntry();
1170   EXPECT_TRUE(entry->GetPageState().IsValid());
1171
1172   // Navigate to same site.
1173   const GURL url2("http://images.google.com");
1174   controller().LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1175   entry = controller().GetLastCommittedEntry();
1176   EXPECT_TRUE(entry->GetPageState().IsValid());
1177
1178   // Committed entry should have page state after DidNavigate.
1179   contents()->TestDidNavigate(orig_rvh, 2, url2, PAGE_TRANSITION_TYPED);
1180   entry = controller().GetLastCommittedEntry();
1181   EXPECT_TRUE(entry->GetPageState().IsValid());
1182
1183   // Now go back.  Committed entry should still have page state.
1184   controller().GoBack();
1185   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
1186   entry = controller().GetLastCommittedEntry();
1187   EXPECT_TRUE(entry->GetPageState().IsValid());
1188 }
1189
1190 // Test that NavigationEntries have the correct page state and SiteInstance
1191 // state after opening a new window to about:blank.  Prevents regression for
1192 // bugs b/1116137 and http://crbug.com/111975.
1193 TEST_F(WebContentsImplTest, NavigationEntryContentStateNewWindow) {
1194   TestRenderViewHost* orig_rvh = test_rvh();
1195
1196   // When opening a new window, it is navigated to about:blank internally.
1197   // Currently, this results in two DidNavigate events.
1198   const GURL url(kAboutBlankURL);
1199   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
1200   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
1201
1202   // Should have a page state here.
1203   NavigationEntry* entry = controller().GetLastCommittedEntry();
1204   EXPECT_TRUE(entry->GetPageState().IsValid());
1205
1206   // The SiteInstance should be available for other navigations to use.
1207   NavigationEntryImpl* entry_impl =
1208       NavigationEntryImpl::FromNavigationEntry(entry);
1209   EXPECT_FALSE(entry_impl->site_instance()->HasSite());
1210   int32 site_instance_id = entry_impl->site_instance()->GetId();
1211
1212   // Navigating to a normal page should not cause a process swap.
1213   const GURL new_url("http://www.google.com");
1214   controller().LoadURL(new_url, Referrer(),
1215                        PAGE_TRANSITION_TYPED, std::string());
1216   EXPECT_FALSE(contents()->cross_navigation_pending());
1217   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
1218   contents()->TestDidNavigate(orig_rvh, 1, new_url, PAGE_TRANSITION_TYPED);
1219   NavigationEntryImpl* entry_impl2 = NavigationEntryImpl::FromNavigationEntry(
1220       controller().GetLastCommittedEntry());
1221   EXPECT_EQ(site_instance_id, entry_impl2->site_instance()->GetId());
1222   EXPECT_TRUE(entry_impl2->site_instance()->HasSite());
1223 }
1224
1225 ////////////////////////////////////////////////////////////////////////////////
1226 // Interstitial Tests
1227 ////////////////////////////////////////////////////////////////////////////////
1228
1229 // Test navigating to a page (with the navigation initiated from the browser,
1230 // as when a URL is typed in the location bar) that shows an interstitial and
1231 // creates a new navigation entry, then hiding it without proceeding.
1232 TEST_F(WebContentsImplTest,
1233        ShowInterstitialFromBrowserWithNewNavigationDontProceed) {
1234   // Navigate to a page.
1235   GURL url1("http://www.google.com");
1236   test_rvh()->SendNavigate(1, url1);
1237   EXPECT_EQ(1, controller().GetEntryCount());
1238
1239   // Initiate a browser navigation that will trigger the interstitial
1240   controller().LoadURL(GURL("http://www.evil.com"), Referrer(),
1241                         PAGE_TRANSITION_TYPED, std::string());
1242
1243   // Show an interstitial.
1244   TestInterstitialPage::InterstitialState state =
1245       TestInterstitialPage::INVALID;
1246   bool deleted = false;
1247   GURL url2("http://interstitial");
1248   TestInterstitialPage* interstitial =
1249       new TestInterstitialPage(contents(), true, url2, &state, &deleted);
1250   TestInterstitialPageStateGuard state_guard(interstitial);
1251   interstitial->Show();
1252   // The interstitial should not show until its navigation has committed.
1253   EXPECT_FALSE(interstitial->is_showing());
1254   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1255   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1256   // Let's commit the interstitial navigation.
1257   interstitial->TestDidNavigate(1, url2);
1258   EXPECT_TRUE(interstitial->is_showing());
1259   EXPECT_TRUE(contents()->ShowingInterstitialPage());
1260   EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1261   NavigationEntry* entry = controller().GetVisibleEntry();
1262   ASSERT_TRUE(entry != NULL);
1263   EXPECT_TRUE(entry->GetURL() == url2);
1264
1265   // Now don't proceed.
1266   interstitial->DontProceed();
1267   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1268   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1269   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1270   entry = controller().GetVisibleEntry();
1271   ASSERT_TRUE(entry != NULL);
1272   EXPECT_TRUE(entry->GetURL() == url1);
1273   EXPECT_EQ(1, controller().GetEntryCount());
1274
1275   RunAllPendingInMessageLoop();
1276   EXPECT_TRUE(deleted);
1277 }
1278
1279 // Test navigating to a page (with the navigation initiated from the renderer,
1280 // as when clicking on a link in the page) that shows an interstitial and
1281 // creates a new navigation entry, then hiding it without proceeding.
1282 TEST_F(WebContentsImplTest,
1283        ShowInterstitiaFromRendererlWithNewNavigationDontProceed) {
1284   // Navigate to a page.
1285   GURL url1("http://www.google.com");
1286   test_rvh()->SendNavigate(1, url1);
1287   EXPECT_EQ(1, controller().GetEntryCount());
1288
1289   // Show an interstitial (no pending entry, the interstitial would have been
1290   // triggered by clicking on a link).
1291   TestInterstitialPage::InterstitialState state =
1292       TestInterstitialPage::INVALID;
1293   bool deleted = false;
1294   GURL url2("http://interstitial");
1295   TestInterstitialPage* interstitial =
1296       new TestInterstitialPage(contents(), true, url2, &state, &deleted);
1297   TestInterstitialPageStateGuard state_guard(interstitial);
1298   interstitial->Show();
1299   // The interstitial should not show until its navigation has committed.
1300   EXPECT_FALSE(interstitial->is_showing());
1301   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1302   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1303   // Let's commit the interstitial navigation.
1304   interstitial->TestDidNavigate(1, url2);
1305   EXPECT_TRUE(interstitial->is_showing());
1306   EXPECT_TRUE(contents()->ShowingInterstitialPage());
1307   EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1308   NavigationEntry* entry = controller().GetVisibleEntry();
1309   ASSERT_TRUE(entry != NULL);
1310   EXPECT_TRUE(entry->GetURL() == url2);
1311
1312   // Now don't proceed.
1313   interstitial->DontProceed();
1314   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1315   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1316   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1317   entry = controller().GetVisibleEntry();
1318   ASSERT_TRUE(entry != NULL);
1319   EXPECT_TRUE(entry->GetURL() == url1);
1320   EXPECT_EQ(1, controller().GetEntryCount());
1321
1322   RunAllPendingInMessageLoop();
1323   EXPECT_TRUE(deleted);
1324 }
1325
1326 // Test navigating to a page that shows an interstitial without creating a new
1327 // navigation entry (this happens when the interstitial is triggered by a
1328 // sub-resource in the page), then hiding it without proceeding.
1329 TEST_F(WebContentsImplTest, ShowInterstitialNoNewNavigationDontProceed) {
1330   // Navigate to a page.
1331   GURL url1("http://www.google.com");
1332   test_rvh()->SendNavigate(1, url1);
1333   EXPECT_EQ(1, controller().GetEntryCount());
1334
1335   // Show an interstitial.
1336   TestInterstitialPage::InterstitialState state =
1337       TestInterstitialPage::INVALID;
1338   bool deleted = false;
1339   GURL url2("http://interstitial");
1340   TestInterstitialPage* interstitial =
1341       new TestInterstitialPage(contents(), false, url2, &state, &deleted);
1342   TestInterstitialPageStateGuard state_guard(interstitial);
1343   interstitial->Show();
1344   // The interstitial should not show until its navigation has committed.
1345   EXPECT_FALSE(interstitial->is_showing());
1346   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1347   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1348   // Let's commit the interstitial navigation.
1349   interstitial->TestDidNavigate(1, url2);
1350   EXPECT_TRUE(interstitial->is_showing());
1351   EXPECT_TRUE(contents()->ShowingInterstitialPage());
1352   EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1353   NavigationEntry* entry = controller().GetVisibleEntry();
1354   ASSERT_TRUE(entry != NULL);
1355   // The URL specified to the interstitial should have been ignored.
1356   EXPECT_TRUE(entry->GetURL() == url1);
1357
1358   // Now don't proceed.
1359   interstitial->DontProceed();
1360   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1361   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1362   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1363   entry = controller().GetVisibleEntry();
1364   ASSERT_TRUE(entry != NULL);
1365   EXPECT_TRUE(entry->GetURL() == url1);
1366   EXPECT_EQ(1, controller().GetEntryCount());
1367
1368   RunAllPendingInMessageLoop();
1369   EXPECT_TRUE(deleted);
1370 }
1371
1372 // Test navigating to a page (with the navigation initiated from the browser,
1373 // as when a URL is typed in the location bar) that shows an interstitial and
1374 // creates a new navigation entry, then proceeding.
1375 TEST_F(WebContentsImplTest,
1376        ShowInterstitialFromBrowserNewNavigationProceed) {
1377   // Navigate to a page.
1378   GURL url1("http://www.google.com");
1379   test_rvh()->SendNavigate(1, url1);
1380   EXPECT_EQ(1, controller().GetEntryCount());
1381
1382   // Initiate a browser navigation that will trigger the interstitial
1383   controller().LoadURL(GURL("http://www.evil.com"), Referrer(),
1384                         PAGE_TRANSITION_TYPED, std::string());
1385
1386   // Show an interstitial.
1387   TestInterstitialPage::InterstitialState state =
1388       TestInterstitialPage::INVALID;
1389   bool deleted = false;
1390   GURL url2("http://interstitial");
1391   TestInterstitialPage* interstitial =
1392       new TestInterstitialPage(contents(), true, url2, &state, &deleted);
1393   TestInterstitialPageStateGuard state_guard(interstitial);
1394   interstitial->Show();
1395   // The interstitial should not show until its navigation has committed.
1396   EXPECT_FALSE(interstitial->is_showing());
1397   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1398   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1399   // Let's commit the interstitial navigation.
1400   interstitial->TestDidNavigate(1, url2);
1401   EXPECT_TRUE(interstitial->is_showing());
1402   EXPECT_TRUE(contents()->ShowingInterstitialPage());
1403   EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1404   NavigationEntry* entry = controller().GetVisibleEntry();
1405   ASSERT_TRUE(entry != NULL);
1406   EXPECT_TRUE(entry->GetURL() == url2);
1407
1408   // Then proceed.
1409   interstitial->Proceed();
1410   // The interstitial should show until the new navigation commits.
1411   RunAllPendingInMessageLoop();
1412   ASSERT_FALSE(deleted);
1413   EXPECT_EQ(TestInterstitialPage::OKED, state);
1414   EXPECT_TRUE(contents()->ShowingInterstitialPage());
1415   EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1416
1417   // Simulate the navigation to the page, that's when the interstitial gets
1418   // hidden.
1419   GURL url3("http://www.thepage.com");
1420   test_rvh()->SendNavigate(2, url3);
1421
1422   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1423   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1424   entry = controller().GetVisibleEntry();
1425   ASSERT_TRUE(entry != NULL);
1426   EXPECT_TRUE(entry->GetURL() == url3);
1427
1428   EXPECT_EQ(2, controller().GetEntryCount());
1429
1430   RunAllPendingInMessageLoop();
1431   EXPECT_TRUE(deleted);
1432 }
1433
1434 // Test navigating to a page (with the navigation initiated from the renderer,
1435 // as when clicking on a link in the page) that shows an interstitial and
1436 // creates a new navigation entry, then proceeding.
1437 TEST_F(WebContentsImplTest,
1438        ShowInterstitialFromRendererNewNavigationProceed) {
1439   // Navigate to a page.
1440   GURL url1("http://www.google.com");
1441   test_rvh()->SendNavigate(1, url1);
1442   EXPECT_EQ(1, controller().GetEntryCount());
1443
1444   // Show an interstitial.
1445   TestInterstitialPage::InterstitialState state =
1446       TestInterstitialPage::INVALID;
1447   bool deleted = false;
1448   GURL url2("http://interstitial");
1449   TestInterstitialPage* interstitial =
1450       new TestInterstitialPage(contents(), true, url2, &state, &deleted);
1451   TestInterstitialPageStateGuard state_guard(interstitial);
1452   interstitial->Show();
1453   // The interstitial should not show until its navigation has committed.
1454   EXPECT_FALSE(interstitial->is_showing());
1455   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1456   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1457   // Let's commit the interstitial navigation.
1458   interstitial->TestDidNavigate(1, url2);
1459   EXPECT_TRUE(interstitial->is_showing());
1460   EXPECT_TRUE(contents()->ShowingInterstitialPage());
1461   EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1462   NavigationEntry* entry = controller().GetVisibleEntry();
1463   ASSERT_TRUE(entry != NULL);
1464   EXPECT_TRUE(entry->GetURL() == url2);
1465
1466   // Then proceed.
1467   interstitial->Proceed();
1468   // The interstitial should show until the new navigation commits.
1469   RunAllPendingInMessageLoop();
1470   ASSERT_FALSE(deleted);
1471   EXPECT_EQ(TestInterstitialPage::OKED, state);
1472   EXPECT_TRUE(contents()->ShowingInterstitialPage());
1473   EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1474
1475   // Simulate the navigation to the page, that's when the interstitial gets
1476   // hidden.
1477   GURL url3("http://www.thepage.com");
1478   test_rvh()->SendNavigate(2, url3);
1479
1480   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1481   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1482   entry = controller().GetVisibleEntry();
1483   ASSERT_TRUE(entry != NULL);
1484   EXPECT_TRUE(entry->GetURL() == url3);
1485
1486   EXPECT_EQ(2, controller().GetEntryCount());
1487
1488   RunAllPendingInMessageLoop();
1489   EXPECT_TRUE(deleted);
1490 }
1491
1492 // Test navigating to a page that shows an interstitial without creating a new
1493 // navigation entry (this happens when the interstitial is triggered by a
1494 // sub-resource in the page), then proceeding.
1495 TEST_F(WebContentsImplTest, ShowInterstitialNoNewNavigationProceed) {
1496   // Navigate to a page so we have a navigation entry in the controller.
1497   GURL url1("http://www.google.com");
1498   test_rvh()->SendNavigate(1, url1);
1499   EXPECT_EQ(1, controller().GetEntryCount());
1500
1501   // Show an interstitial.
1502   TestInterstitialPage::InterstitialState state =
1503       TestInterstitialPage::INVALID;
1504   bool deleted = false;
1505   GURL url2("http://interstitial");
1506   TestInterstitialPage* interstitial =
1507       new TestInterstitialPage(contents(), false, url2, &state, &deleted);
1508   TestInterstitialPageStateGuard state_guard(interstitial);
1509   interstitial->Show();
1510   // The interstitial should not show until its navigation has committed.
1511   EXPECT_FALSE(interstitial->is_showing());
1512   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1513   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1514   // Let's commit the interstitial navigation.
1515   interstitial->TestDidNavigate(1, url2);
1516   EXPECT_TRUE(interstitial->is_showing());
1517   EXPECT_TRUE(contents()->ShowingInterstitialPage());
1518   EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1519   NavigationEntry* entry = controller().GetVisibleEntry();
1520   ASSERT_TRUE(entry != NULL);
1521   // The URL specified to the interstitial should have been ignored.
1522   EXPECT_TRUE(entry->GetURL() == url1);
1523
1524   // Then proceed.
1525   interstitial->Proceed();
1526   // Since this is not a new navigation, the previous page is dismissed right
1527   // away and shows the original page.
1528   EXPECT_EQ(TestInterstitialPage::OKED, state);
1529   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1530   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1531   entry = controller().GetVisibleEntry();
1532   ASSERT_TRUE(entry != NULL);
1533   EXPECT_TRUE(entry->GetURL() == url1);
1534
1535   EXPECT_EQ(1, controller().GetEntryCount());
1536
1537   RunAllPendingInMessageLoop();
1538   EXPECT_TRUE(deleted);
1539 }
1540
1541 // Test navigating to a page that shows an interstitial, then navigating away.
1542 TEST_F(WebContentsImplTest, ShowInterstitialThenNavigate) {
1543   // Show interstitial.
1544   TestInterstitialPage::InterstitialState state =
1545       TestInterstitialPage::INVALID;
1546   bool deleted = false;
1547   GURL url("http://interstitial");
1548   TestInterstitialPage* interstitial =
1549       new TestInterstitialPage(contents(), true, url, &state, &deleted);
1550   TestInterstitialPageStateGuard state_guard(interstitial);
1551   interstitial->Show();
1552   interstitial->TestDidNavigate(1, url);
1553
1554   // While interstitial showing, navigate to a new URL.
1555   const GURL url2("http://www.yahoo.com");
1556   test_rvh()->SendNavigate(1, url2);
1557
1558   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1559
1560   RunAllPendingInMessageLoop();
1561   EXPECT_TRUE(deleted);
1562 }
1563
1564 // Test navigating to a page that shows an interstitial, then going back.
1565 TEST_F(WebContentsImplTest, ShowInterstitialThenGoBack) {
1566   // Navigate to a page so we have a navigation entry in the controller.
1567   GURL url1("http://www.google.com");
1568   test_rvh()->SendNavigate(1, url1);
1569   EXPECT_EQ(1, controller().GetEntryCount());
1570
1571   // Show interstitial.
1572   TestInterstitialPage::InterstitialState state =
1573       TestInterstitialPage::INVALID;
1574   bool deleted = false;
1575   GURL interstitial_url("http://interstitial");
1576   TestInterstitialPage* interstitial =
1577       new TestInterstitialPage(contents(), true, interstitial_url,
1578                                &state, &deleted);
1579   TestInterstitialPageStateGuard state_guard(interstitial);
1580   interstitial->Show();
1581   interstitial->TestDidNavigate(2, interstitial_url);
1582
1583   // While the interstitial is showing, go back.
1584   controller().GoBack();
1585   test_rvh()->SendNavigate(1, url1);
1586
1587   // Make sure we are back to the original page and that the interstitial is
1588   // gone.
1589   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1590   NavigationEntry* entry = controller().GetVisibleEntry();
1591   ASSERT_TRUE(entry);
1592   EXPECT_EQ(url1.spec(), entry->GetURL().spec());
1593
1594   RunAllPendingInMessageLoop();
1595   EXPECT_TRUE(deleted);
1596 }
1597
1598 // Test navigating to a page that shows an interstitial, has a renderer crash,
1599 // and then goes back.
1600 TEST_F(WebContentsImplTest, ShowInterstitialCrashRendererThenGoBack) {
1601   // Navigate to a page so we have a navigation entry in the controller.
1602   GURL url1("http://www.google.com");
1603   test_rvh()->SendNavigate(1, url1);
1604   EXPECT_EQ(1, controller().GetEntryCount());
1605
1606   // Show interstitial.
1607   TestInterstitialPage::InterstitialState state =
1608       TestInterstitialPage::INVALID;
1609   bool deleted = false;
1610   GURL interstitial_url("http://interstitial");
1611   TestInterstitialPage* interstitial =
1612       new TestInterstitialPage(contents(), true, interstitial_url,
1613                                &state, &deleted);
1614   TestInterstitialPageStateGuard state_guard(interstitial);
1615   interstitial->Show();
1616   interstitial->TestDidNavigate(2, interstitial_url);
1617
1618   // Crash the renderer
1619   test_rvh()->OnMessageReceived(
1620       ViewHostMsg_RenderProcessGone(
1621           0, base::TERMINATION_STATUS_PROCESS_CRASHED, -1));
1622
1623   // While the interstitial is showing, go back.
1624   controller().GoBack();
1625   test_rvh()->SendNavigate(1, url1);
1626
1627   // Make sure we are back to the original page and that the interstitial is
1628   // gone.
1629   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1630   NavigationEntry* entry = controller().GetVisibleEntry();
1631   ASSERT_TRUE(entry);
1632   EXPECT_EQ(url1.spec(), entry->GetURL().spec());
1633
1634   RunAllPendingInMessageLoop();
1635   EXPECT_TRUE(deleted);
1636 }
1637
1638 // Test navigating to a page that shows an interstitial, has the renderer crash,
1639 // and then navigates to the interstitial.
1640 TEST_F(WebContentsImplTest, ShowInterstitialCrashRendererThenNavigate) {
1641   // Navigate to a page so we have a navigation entry in the controller.
1642   GURL url1("http://www.google.com");
1643   test_rvh()->SendNavigate(1, url1);
1644   EXPECT_EQ(1, controller().GetEntryCount());
1645
1646   // Show interstitial.
1647   TestInterstitialPage::InterstitialState state =
1648       TestInterstitialPage::INVALID;
1649   bool deleted = false;
1650   GURL interstitial_url("http://interstitial");
1651   TestInterstitialPage* interstitial =
1652       new TestInterstitialPage(contents(), true, interstitial_url,
1653                                &state, &deleted);
1654   TestInterstitialPageStateGuard state_guard(interstitial);
1655   interstitial->Show();
1656
1657   // Crash the renderer
1658   test_rvh()->OnMessageReceived(
1659       ViewHostMsg_RenderProcessGone(
1660           0, base::TERMINATION_STATUS_PROCESS_CRASHED, -1));
1661
1662   interstitial->TestDidNavigate(2, interstitial_url);
1663 }
1664
1665 // Test navigating to a page that shows an interstitial, then close the
1666 // contents.
1667 TEST_F(WebContentsImplTest, ShowInterstitialThenCloseTab) {
1668   // Show interstitial.
1669   TestInterstitialPage::InterstitialState state =
1670       TestInterstitialPage::INVALID;
1671   bool deleted = false;
1672   GURL url("http://interstitial");
1673   TestInterstitialPage* interstitial =
1674       new TestInterstitialPage(contents(), true, url, &state, &deleted);
1675   TestInterstitialPageStateGuard state_guard(interstitial);
1676   interstitial->Show();
1677   interstitial->TestDidNavigate(1, url);
1678
1679   // Now close the contents.
1680   DeleteContents();
1681   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1682
1683   RunAllPendingInMessageLoop();
1684   EXPECT_TRUE(deleted);
1685 }
1686
1687 // Test navigating to a page that shows an interstitial, then close the
1688 // contents.
1689 TEST_F(WebContentsImplTest, ShowInterstitialThenCloseAndShutdown) {
1690   // Show interstitial.
1691   TestInterstitialPage::InterstitialState state =
1692       TestInterstitialPage::INVALID;
1693   bool deleted = false;
1694   GURL url("http://interstitial");
1695   TestInterstitialPage* interstitial =
1696       new TestInterstitialPage(contents(), true, url, &state, &deleted);
1697   TestInterstitialPageStateGuard state_guard(interstitial);
1698   interstitial->Show();
1699   interstitial->TestDidNavigate(1, url);
1700   RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
1701       interstitial->GetRenderViewHostForTesting());
1702
1703   // Now close the contents.
1704   DeleteContents();
1705   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1706
1707   // Before the interstitial has a chance to process its shutdown task,
1708   // simulate quitting the browser.  This goes through all processes and
1709   // tells them to destruct.
1710   rvh->OnMessageReceived(
1711         ViewHostMsg_RenderProcessGone(0, 0, 0));
1712
1713   RunAllPendingInMessageLoop();
1714   EXPECT_TRUE(deleted);
1715 }
1716
1717 // Test that after Proceed is called and an interstitial is still shown, no more
1718 // commands get executed.
1719 TEST_F(WebContentsImplTest, ShowInterstitialProceedMultipleCommands) {
1720   // Navigate to a page so we have a navigation entry in the controller.
1721   GURL url1("http://www.google.com");
1722   test_rvh()->SendNavigate(1, url1);
1723   EXPECT_EQ(1, controller().GetEntryCount());
1724
1725   // Show an interstitial.
1726   TestInterstitialPage::InterstitialState state =
1727       TestInterstitialPage::INVALID;
1728   bool deleted = false;
1729   GURL url2("http://interstitial");
1730   TestInterstitialPage* interstitial =
1731       new TestInterstitialPage(contents(), true, url2, &state, &deleted);
1732   TestInterstitialPageStateGuard state_guard(interstitial);
1733   interstitial->Show();
1734   interstitial->TestDidNavigate(1, url2);
1735
1736   // Run a command.
1737   EXPECT_EQ(0, interstitial->command_received_count());
1738   interstitial->TestDomOperationResponse("toto");
1739   EXPECT_EQ(1, interstitial->command_received_count());
1740
1741   // Then proceed.
1742   interstitial->Proceed();
1743   RunAllPendingInMessageLoop();
1744   ASSERT_FALSE(deleted);
1745
1746   // While the navigation to the new page is pending, send other commands, they
1747   // should be ignored.
1748   interstitial->TestDomOperationResponse("hello");
1749   interstitial->TestDomOperationResponse("hi");
1750   EXPECT_EQ(1, interstitial->command_received_count());
1751 }
1752
1753 // Test showing an interstitial while another interstitial is already showing.
1754 TEST_F(WebContentsImplTest, ShowInterstitialOnInterstitial) {
1755   // Navigate to a page so we have a navigation entry in the controller.
1756   GURL start_url("http://www.google.com");
1757   test_rvh()->SendNavigate(1, start_url);
1758   EXPECT_EQ(1, controller().GetEntryCount());
1759
1760   // Show an interstitial.
1761   TestInterstitialPage::InterstitialState state1 =
1762       TestInterstitialPage::INVALID;
1763   bool deleted1 = false;
1764   GURL url1("http://interstitial1");
1765   TestInterstitialPage* interstitial1 =
1766       new TestInterstitialPage(contents(), true, url1, &state1, &deleted1);
1767   TestInterstitialPageStateGuard state_guard1(interstitial1);
1768   interstitial1->Show();
1769   interstitial1->TestDidNavigate(1, url1);
1770
1771   // Now show another interstitial.
1772   TestInterstitialPage::InterstitialState state2 =
1773       TestInterstitialPage::INVALID;
1774   bool deleted2 = false;
1775   GURL url2("http://interstitial2");
1776   TestInterstitialPage* interstitial2 =
1777       new TestInterstitialPage(contents(), true, url2, &state2, &deleted2);
1778   TestInterstitialPageStateGuard state_guard2(interstitial2);
1779   interstitial2->Show();
1780   interstitial2->TestDidNavigate(1, url2);
1781
1782   // Showing interstitial2 should have caused interstitial1 to go away.
1783   EXPECT_EQ(TestInterstitialPage::CANCELED, state1);
1784   EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2);
1785
1786   RunAllPendingInMessageLoop();
1787   EXPECT_TRUE(deleted1);
1788   ASSERT_FALSE(deleted2);
1789
1790   // Let's make sure interstitial2 is working as intended.
1791   interstitial2->Proceed();
1792   GURL landing_url("http://www.thepage.com");
1793   test_rvh()->SendNavigate(2, landing_url);
1794
1795   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1796   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1797   NavigationEntry* entry = controller().GetVisibleEntry();
1798   ASSERT_TRUE(entry != NULL);
1799   EXPECT_TRUE(entry->GetURL() == landing_url);
1800   EXPECT_EQ(2, controller().GetEntryCount());
1801   RunAllPendingInMessageLoop();
1802   EXPECT_TRUE(deleted2);
1803 }
1804
1805 // Test showing an interstitial, proceeding and then navigating to another
1806 // interstitial.
1807 TEST_F(WebContentsImplTest, ShowInterstitialProceedShowInterstitial) {
1808   // Navigate to a page so we have a navigation entry in the controller.
1809   GURL start_url("http://www.google.com");
1810   test_rvh()->SendNavigate(1, start_url);
1811   EXPECT_EQ(1, controller().GetEntryCount());
1812
1813   // Show an interstitial.
1814   TestInterstitialPage::InterstitialState state1 =
1815       TestInterstitialPage::INVALID;
1816   bool deleted1 = false;
1817   GURL url1("http://interstitial1");
1818   TestInterstitialPage* interstitial1 =
1819       new TestInterstitialPage(contents(), true, url1, &state1, &deleted1);
1820   TestInterstitialPageStateGuard state_guard1(interstitial1);
1821   interstitial1->Show();
1822   interstitial1->TestDidNavigate(1, url1);
1823
1824   // Take action.  The interstitial won't be hidden until the navigation is
1825   // committed.
1826   interstitial1->Proceed();
1827   EXPECT_EQ(TestInterstitialPage::OKED, state1);
1828
1829   // Now show another interstitial (simulating the navigation causing another
1830   // interstitial).
1831   TestInterstitialPage::InterstitialState state2 =
1832       TestInterstitialPage::INVALID;
1833   bool deleted2 = false;
1834   GURL url2("http://interstitial2");
1835   TestInterstitialPage* interstitial2 =
1836       new TestInterstitialPage(contents(), true, url2, &state2, &deleted2);
1837   TestInterstitialPageStateGuard state_guard2(interstitial2);
1838   interstitial2->Show();
1839   interstitial2->TestDidNavigate(1, url2);
1840
1841   // Showing interstitial2 should have caused interstitial1 to go away.
1842   EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2);
1843   RunAllPendingInMessageLoop();
1844   EXPECT_TRUE(deleted1);
1845   ASSERT_FALSE(deleted2);
1846
1847   // Let's make sure interstitial2 is working as intended.
1848   interstitial2->Proceed();
1849   GURL landing_url("http://www.thepage.com");
1850   test_rvh()->SendNavigate(2, landing_url);
1851
1852   RunAllPendingInMessageLoop();
1853   EXPECT_TRUE(deleted2);
1854   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1855   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1856   NavigationEntry* entry = controller().GetVisibleEntry();
1857   ASSERT_TRUE(entry != NULL);
1858   EXPECT_TRUE(entry->GetURL() == landing_url);
1859   EXPECT_EQ(2, controller().GetEntryCount());
1860 }
1861
1862 // Test that navigating away from an interstitial while it's loading cause it
1863 // not to show.
1864 TEST_F(WebContentsImplTest, NavigateBeforeInterstitialShows) {
1865   // Show an interstitial.
1866   TestInterstitialPage::InterstitialState state =
1867       TestInterstitialPage::INVALID;
1868   bool deleted = false;
1869   GURL interstitial_url("http://interstitial");
1870   TestInterstitialPage* interstitial =
1871       new TestInterstitialPage(contents(), true, interstitial_url,
1872                                &state, &deleted);
1873   TestInterstitialPageStateGuard state_guard(interstitial);
1874   interstitial->Show();
1875
1876   // Let's simulate a navigation initiated from the browser before the
1877   // interstitial finishes loading.
1878   const GURL url("http://www.google.com");
1879   controller().LoadURL(url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1880   EXPECT_FALSE(interstitial->is_showing());
1881   RunAllPendingInMessageLoop();
1882   ASSERT_FALSE(deleted);
1883
1884   // Now let's make the interstitial navigation commit.
1885   interstitial->TestDidNavigate(1, interstitial_url);
1886
1887   // After it loaded the interstitial should be gone.
1888   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1889
1890   RunAllPendingInMessageLoop();
1891   EXPECT_TRUE(deleted);
1892 }
1893
1894 // Test that a new request to show an interstitial while an interstitial is
1895 // pending does not cause problems. htp://crbug/29655 and htp://crbug/9442.
1896 TEST_F(WebContentsImplTest, TwoQuickInterstitials) {
1897   GURL interstitial_url("http://interstitial");
1898
1899   // Show a first interstitial.
1900   TestInterstitialPage::InterstitialState state1 =
1901       TestInterstitialPage::INVALID;
1902   bool deleted1 = false;
1903   TestInterstitialPage* interstitial1 =
1904       new TestInterstitialPage(contents(), true, interstitial_url,
1905                                &state1, &deleted1);
1906   TestInterstitialPageStateGuard state_guard1(interstitial1);
1907   interstitial1->Show();
1908
1909   // Show another interstitial on that same contents before the first one had
1910   // time to load.
1911   TestInterstitialPage::InterstitialState state2 =
1912       TestInterstitialPage::INVALID;
1913   bool deleted2 = false;
1914   TestInterstitialPage* interstitial2 =
1915       new TestInterstitialPage(contents(), true, interstitial_url,
1916                                &state2, &deleted2);
1917   TestInterstitialPageStateGuard state_guard2(interstitial2);
1918   interstitial2->Show();
1919
1920   // The first interstitial should have been closed and deleted.
1921   EXPECT_EQ(TestInterstitialPage::CANCELED, state1);
1922   // The 2nd one should still be OK.
1923   EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2);
1924
1925   RunAllPendingInMessageLoop();
1926   EXPECT_TRUE(deleted1);
1927   ASSERT_FALSE(deleted2);
1928
1929   // Make the interstitial navigation commit it should be showing.
1930   interstitial2->TestDidNavigate(1, interstitial_url);
1931   EXPECT_EQ(interstitial2, contents()->GetInterstitialPage());
1932 }
1933
1934 // Test showing an interstitial and have its renderer crash.
1935 TEST_F(WebContentsImplTest, InterstitialCrasher) {
1936   // Show an interstitial.
1937   TestInterstitialPage::InterstitialState state =
1938       TestInterstitialPage::INVALID;
1939   bool deleted = false;
1940   GURL url("http://interstitial");
1941   TestInterstitialPage* interstitial =
1942       new TestInterstitialPage(contents(), true, url, &state, &deleted);
1943   TestInterstitialPageStateGuard state_guard(interstitial);
1944   interstitial->Show();
1945   // Simulate a renderer crash before the interstitial is shown.
1946   interstitial->TestRenderViewTerminated(
1947       base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
1948   // The interstitial should have been dismissed.
1949   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1950   RunAllPendingInMessageLoop();
1951   EXPECT_TRUE(deleted);
1952
1953   // Now try again but this time crash the intersitial after it was shown.
1954   interstitial =
1955       new TestInterstitialPage(contents(), true, url, &state, &deleted);
1956   interstitial->Show();
1957   interstitial->TestDidNavigate(1, url);
1958   // Simulate a renderer crash.
1959   interstitial->TestRenderViewTerminated(
1960       base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
1961   // The interstitial should have been dismissed.
1962   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1963   RunAllPendingInMessageLoop();
1964   EXPECT_TRUE(deleted);
1965 }
1966
1967 // Tests that showing an interstitial as a result of a browser initiated
1968 // navigation while an interstitial is showing does not remove the pending
1969 // entry (see http://crbug.com/9791).
1970 TEST_F(WebContentsImplTest, NewInterstitialDoesNotCancelPendingEntry) {
1971   const char kUrl[] = "http://www.badguys.com/";
1972   const GURL kGURL(kUrl);
1973
1974   // Start a navigation to a page
1975   contents()->GetController().LoadURL(
1976       kGURL, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1977
1978   // Simulate that navigation triggering an interstitial.
1979   TestInterstitialPage::InterstitialState state =
1980       TestInterstitialPage::INVALID;
1981   bool deleted = false;
1982   TestInterstitialPage* interstitial =
1983       new TestInterstitialPage(contents(), true, kGURL, &state, &deleted);
1984   TestInterstitialPageStateGuard state_guard(interstitial);
1985   interstitial->Show();
1986   interstitial->TestDidNavigate(1, kGURL);
1987
1988   // Initiate a new navigation from the browser that also triggers an
1989   // interstitial.
1990   contents()->GetController().LoadURL(
1991       kGURL, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1992   TestInterstitialPage::InterstitialState state2 =
1993       TestInterstitialPage::INVALID;
1994   bool deleted2 = false;
1995   TestInterstitialPage* interstitial2 =
1996       new TestInterstitialPage(contents(), true, kGURL, &state2, &deleted2);
1997   TestInterstitialPageStateGuard state_guard2(interstitial2);
1998   interstitial2->Show();
1999   interstitial2->TestDidNavigate(1, kGURL);
2000
2001   // Make sure we still have an entry.
2002   NavigationEntry* entry = contents()->GetController().GetPendingEntry();
2003   ASSERT_TRUE(entry);
2004   EXPECT_EQ(kUrl, entry->GetURL().spec());
2005
2006   // And that the first interstitial is gone, but not the second.
2007   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
2008   EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2);
2009   RunAllPendingInMessageLoop();
2010   EXPECT_TRUE(deleted);
2011   EXPECT_FALSE(deleted2);
2012 }
2013
2014 // Tests that Javascript messages are not shown while an interstitial is
2015 // showing.
2016 TEST_F(WebContentsImplTest, NoJSMessageOnInterstitials) {
2017   const char kUrl[] = "http://www.badguys.com/";
2018   const GURL kGURL(kUrl);
2019
2020   // Start a navigation to a page
2021   contents()->GetController().LoadURL(
2022       kGURL, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2023   // DidNavigate from the page
2024   contents()->TestDidNavigate(rvh(), 1, kGURL, PAGE_TRANSITION_TYPED);
2025
2026   // Simulate showing an interstitial while the page is showing.
2027   TestInterstitialPage::InterstitialState state =
2028       TestInterstitialPage::INVALID;
2029   bool deleted = false;
2030   TestInterstitialPage* interstitial =
2031       new TestInterstitialPage(contents(), true, kGURL, &state, &deleted);
2032   TestInterstitialPageStateGuard state_guard(interstitial);
2033   interstitial->Show();
2034   interstitial->TestDidNavigate(1, kGURL);
2035
2036   // While the interstitial is showing, let's simulate the hidden page
2037   // attempting to show a JS message.
2038   IPC::Message* dummy_message = new IPC::Message;
2039   bool did_suppress_message = false;
2040   contents()->RunJavaScriptMessage(contents()->GetRenderViewHost(),
2041       base::ASCIIToUTF16("This is an informative message"),
2042       base::ASCIIToUTF16("OK"),
2043       kGURL, JAVASCRIPT_MESSAGE_TYPE_ALERT, dummy_message,
2044       &did_suppress_message);
2045   EXPECT_TRUE(did_suppress_message);
2046 }
2047
2048 // Makes sure that if the source passed to CopyStateFromAndPrune has an
2049 // interstitial it isn't copied over to the destination.
2050 TEST_F(WebContentsImplTest, CopyStateFromAndPruneSourceInterstitial) {
2051   // Navigate to a page.
2052   GURL url1("http://www.google.com");
2053   test_rvh()->SendNavigate(1, url1);
2054   EXPECT_EQ(1, controller().GetEntryCount());
2055
2056   // Initiate a browser navigation that will trigger the interstitial
2057   controller().LoadURL(GURL("http://www.evil.com"), Referrer(),
2058                         PAGE_TRANSITION_TYPED, std::string());
2059
2060   // Show an interstitial.
2061   TestInterstitialPage::InterstitialState state =
2062       TestInterstitialPage::INVALID;
2063   bool deleted = false;
2064   GURL url2("http://interstitial");
2065   TestInterstitialPage* interstitial =
2066       new TestInterstitialPage(contents(), true, url2, &state, &deleted);
2067   TestInterstitialPageStateGuard state_guard(interstitial);
2068   interstitial->Show();
2069   interstitial->TestDidNavigate(1, url2);
2070   EXPECT_TRUE(interstitial->is_showing());
2071   EXPECT_EQ(2, controller().GetEntryCount());
2072
2073   // Create another NavigationController.
2074   GURL url3("http://foo2");
2075   scoped_ptr<TestWebContents> other_contents(
2076       static_cast<TestWebContents*>(CreateTestWebContents()));
2077   NavigationControllerImpl& other_controller = other_contents->GetController();
2078   other_contents->NavigateAndCommit(url3);
2079   other_contents->ExpectSetHistoryLengthAndPrune(
2080       NavigationEntryImpl::FromNavigationEntry(
2081           other_controller.GetEntryAtIndex(0))->site_instance(), 1,
2082       other_controller.GetEntryAtIndex(0)->GetPageID());
2083   other_controller.CopyStateFromAndPrune(&controller(), false);
2084
2085   // The merged controller should only have two entries: url1 and url2.
2086   ASSERT_EQ(2, other_controller.GetEntryCount());
2087   EXPECT_EQ(1, other_controller.GetCurrentEntryIndex());
2088   EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
2089   EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
2090
2091   // And the merged controller shouldn't be showing an interstitial.
2092   EXPECT_FALSE(other_contents->ShowingInterstitialPage());
2093 }
2094
2095 // Makes sure that CopyStateFromAndPrune cannot be called if the target is
2096 // showing an interstitial.
2097 TEST_F(WebContentsImplTest, CopyStateFromAndPruneTargetInterstitial) {
2098   // Navigate to a page.
2099   GURL url1("http://www.google.com");
2100   contents()->NavigateAndCommit(url1);
2101
2102   // Create another NavigationController.
2103   scoped_ptr<TestWebContents> other_contents(
2104       static_cast<TestWebContents*>(CreateTestWebContents()));
2105   NavigationControllerImpl& other_controller = other_contents->GetController();
2106
2107   // Navigate it to url2.
2108   GURL url2("http://foo2");
2109   other_contents->NavigateAndCommit(url2);
2110
2111   // Show an interstitial.
2112   TestInterstitialPage::InterstitialState state =
2113       TestInterstitialPage::INVALID;
2114   bool deleted = false;
2115   GURL url3("http://interstitial");
2116   TestInterstitialPage* interstitial =
2117       new TestInterstitialPage(other_contents.get(), true, url3, &state,
2118                                &deleted);
2119   TestInterstitialPageStateGuard state_guard(interstitial);
2120   interstitial->Show();
2121   interstitial->TestDidNavigate(1, url3);
2122   EXPECT_TRUE(interstitial->is_showing());
2123   EXPECT_EQ(2, other_controller.GetEntryCount());
2124
2125   // Ensure that we do not allow calling CopyStateFromAndPrune when an
2126   // interstitial is showing in the target.
2127   EXPECT_FALSE(other_controller.CanPruneAllButLastCommitted());
2128 }
2129
2130 // Regression test for http://crbug.com/168611 - the URLs passed by the
2131 // DidFinishLoad and DidFailLoadWithError IPCs should get filtered.
2132 TEST_F(WebContentsImplTest, FilterURLs) {
2133   TestWebContentsObserver observer(contents());
2134
2135   // A navigation to about:whatever should always look like a navigation to
2136   // about:blank
2137   GURL url_normalized(kAboutBlankURL);
2138   GURL url_from_ipc("about:whatever");
2139
2140   // We navigate the test WebContents to about:blank, since NavigateAndCommit
2141   // will use the given URL to create the NavigationEntry as well, and that
2142   // entry should contain the filtered URL.
2143   contents()->NavigateAndCommit(url_normalized);
2144
2145   // Check that an IPC with about:whatever is correctly normalized.
2146   contents()->TestDidFinishLoad(1, url_from_ipc, true);
2147
2148   EXPECT_EQ(url_normalized, observer.last_url());
2149
2150   // Create and navigate another WebContents.
2151   scoped_ptr<TestWebContents> other_contents(
2152       static_cast<TestWebContents*>(CreateTestWebContents()));
2153   TestWebContentsObserver other_observer(other_contents.get());
2154   other_contents->NavigateAndCommit(url_normalized);
2155
2156   // Check that an IPC with about:whatever is correctly normalized.
2157   other_contents->TestDidFailLoadWithError(
2158       1, url_from_ipc, true, 1, base::string16());
2159   EXPECT_EQ(url_normalized, other_observer.last_url());
2160 }
2161
2162 // Test that if a pending contents is deleted before it is shown, we don't
2163 // crash.
2164 TEST_F(WebContentsImplTest, PendingContents) {
2165   scoped_ptr<TestWebContents> other_contents(
2166       static_cast<TestWebContents*>(CreateTestWebContents()));
2167   contents()->AddPendingContents(other_contents.get());
2168   int route_id = other_contents->GetRenderViewHost()->GetRoutingID();
2169   other_contents.reset();
2170   EXPECT_EQ(NULL, contents()->GetCreatedWindow(route_id));
2171 }
2172
2173 }  // namespace content