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