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