Upstream version 8.37.180.0
[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 class FakeValidationMessageDelegate : public WebContentsDelegate {
322  public:
323   FakeValidationMessageDelegate()
324       : hide_validation_message_was_called_(false) {}
325   virtual ~FakeValidationMessageDelegate() {}
326
327   virtual void HideValidationMessage(WebContents* web_contents) OVERRIDE {
328     hide_validation_message_was_called_ = true;
329   }
330
331   bool hide_validation_message_was_called() const {
332     return hide_validation_message_was_called_;
333   }
334
335  private:
336   bool hide_validation_message_was_called_;
337
338   DISALLOW_COPY_AND_ASSIGN(FakeValidationMessageDelegate);
339 };
340
341 }  // namespace
342
343 // Test to make sure that title updates get stripped of whitespace.
344 TEST_F(WebContentsImplTest, UpdateTitle) {
345   NavigationControllerImpl& cont =
346       static_cast<NavigationControllerImpl&>(controller());
347   FrameHostMsg_DidCommitProvisionalLoad_Params params;
348   InitNavigateParams(
349       &params, 0, GURL(url::kAboutBlankURL), PAGE_TRANSITION_TYPED);
350
351   LoadCommittedDetails details;
352   cont.RendererDidNavigate(main_test_rfh(), params, &details);
353
354   contents()->UpdateTitle(main_test_rfh(), 0,
355                           base::ASCIIToUTF16("    Lots O' Whitespace\n"),
356                           base::i18n::LEFT_TO_RIGHT);
357   EXPECT_EQ(base::ASCIIToUTF16("Lots O' Whitespace"), contents()->GetTitle());
358 }
359
360 TEST_F(WebContentsImplTest, DontUseTitleFromPendingEntry) {
361   const GURL kGURL("chrome://blah");
362   controller().LoadURL(
363       kGURL, Referrer(), PAGE_TRANSITION_TYPED, std::string());
364   EXPECT_EQ(base::string16(), contents()->GetTitle());
365 }
366
367 TEST_F(WebContentsImplTest, UseTitleFromPendingEntryIfSet) {
368   const GURL kGURL("chrome://blah");
369   const base::string16 title = base::ASCIIToUTF16("My Title");
370   controller().LoadURL(
371       kGURL, Referrer(), PAGE_TRANSITION_TYPED, std::string());
372
373   NavigationEntry* entry = controller().GetVisibleEntry();
374   ASSERT_EQ(kGURL, entry->GetURL());
375   entry->SetTitle(title);
376
377   EXPECT_EQ(title, contents()->GetTitle());
378 }
379
380 // Test view source mode for a webui page.
381 TEST_F(WebContentsImplTest, NTPViewSource) {
382   NavigationControllerImpl& cont =
383       static_cast<NavigationControllerImpl&>(controller());
384   const char kUrl[] = "view-source:chrome://blah";
385   const GURL kGURL(kUrl);
386
387   process()->sink().ClearMessages();
388
389   cont.LoadURL(
390       kGURL, Referrer(), PAGE_TRANSITION_TYPED, std::string());
391   rvh()->GetDelegate()->RenderViewCreated(rvh());
392   // Did we get the expected message?
393   EXPECT_TRUE(process()->sink().GetFirstMessageMatching(
394       ViewMsg_EnableViewSourceMode::ID));
395
396   FrameHostMsg_DidCommitProvisionalLoad_Params params;
397   InitNavigateParams(&params, 0, kGURL, PAGE_TRANSITION_TYPED);
398   LoadCommittedDetails details;
399   cont.RendererDidNavigate(main_test_rfh(), params, &details);
400   // Also check title and url.
401   EXPECT_EQ(base::ASCIIToUTF16(kUrl), contents()->GetTitle());
402 }
403
404 // Test to ensure UpdateMaxPageID is working properly.
405 TEST_F(WebContentsImplTest, UpdateMaxPageID) {
406   SiteInstance* instance1 = contents()->GetSiteInstance();
407   scoped_refptr<SiteInstance> instance2(SiteInstance::Create(NULL));
408
409   // Starts at -1.
410   EXPECT_EQ(-1, contents()->GetMaxPageID());
411   EXPECT_EQ(-1, contents()->GetMaxPageIDForSiteInstance(instance1));
412   EXPECT_EQ(-1, contents()->GetMaxPageIDForSiteInstance(instance2.get()));
413
414   // Make sure max_page_id_ is monotonically increasing per SiteInstance.
415   contents()->UpdateMaxPageID(3);
416   contents()->UpdateMaxPageID(1);
417   EXPECT_EQ(3, contents()->GetMaxPageID());
418   EXPECT_EQ(3, contents()->GetMaxPageIDForSiteInstance(instance1));
419   EXPECT_EQ(-1, contents()->GetMaxPageIDForSiteInstance(instance2.get()));
420
421   contents()->UpdateMaxPageIDForSiteInstance(instance2.get(), 7);
422   EXPECT_EQ(3, contents()->GetMaxPageID());
423   EXPECT_EQ(3, contents()->GetMaxPageIDForSiteInstance(instance1));
424   EXPECT_EQ(7, contents()->GetMaxPageIDForSiteInstance(instance2.get()));
425 }
426
427 // Test simple same-SiteInstance navigation.
428 TEST_F(WebContentsImplTest, SimpleNavigation) {
429   TestRenderViewHost* orig_rvh = test_rvh();
430   SiteInstance* instance1 = contents()->GetSiteInstance();
431   EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
432
433   // Navigate to URL
434   const GURL url("http://www.google.com");
435   controller().LoadURL(
436       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
437   EXPECT_FALSE(contents()->cross_navigation_pending());
438   EXPECT_EQ(instance1, orig_rvh->GetSiteInstance());
439   // Controller's pending entry will have a NULL site instance until we assign
440   // it in DidNavigate.
441   EXPECT_TRUE(
442       NavigationEntryImpl::FromNavigationEntry(controller().GetVisibleEntry())->
443           site_instance() == NULL);
444
445   // DidNavigate from the page
446   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
447   EXPECT_FALSE(contents()->cross_navigation_pending());
448   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
449   EXPECT_EQ(instance1, orig_rvh->GetSiteInstance());
450   // Controller's entry should now have the SiteInstance, or else we won't be
451   // able to find it later.
452   EXPECT_EQ(
453       instance1,
454       NavigationEntryImpl::FromNavigationEntry(controller().GetVisibleEntry())->
455           site_instance());
456 }
457
458 // Test that we reject NavigateToEntry if the url is over kMaxURLChars.
459 TEST_F(WebContentsImplTest, NavigateToExcessivelyLongURL) {
460   // Construct a URL that's kMaxURLChars + 1 long of all 'a's.
461   const GURL url(std::string("http://example.org/").append(
462       GetMaxURLChars() + 1, 'a'));
463
464   controller().LoadURL(
465       url, Referrer(), PAGE_TRANSITION_GENERATED, std::string());
466   EXPECT_TRUE(controller().GetVisibleEntry() == NULL);
467 }
468
469 // Test that navigating across a site boundary creates a new RenderViewHost
470 // with a new SiteInstance.  Going back should do the same.
471 TEST_F(WebContentsImplTest, CrossSiteBoundaries) {
472   TestRenderViewHost* orig_rvh = test_rvh();
473   RenderFrameHostImpl* orig_rfh =
474       contents()->GetFrameTree()->root()->current_frame_host();
475   int orig_rvh_delete_count = 0;
476   orig_rvh->set_delete_counter(&orig_rvh_delete_count);
477   SiteInstance* instance1 = contents()->GetSiteInstance();
478
479   // Navigate to URL.  First URL should use first RenderViewHost.
480   const GURL url("http://www.google.com");
481   controller().LoadURL(
482       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
483   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
484
485   // Keep the number of active views in orig_rvh's SiteInstance
486   // non-zero so that orig_rvh doesn't get deleted when it gets
487   // swapped out.
488   static_cast<SiteInstanceImpl*>(orig_rvh->GetSiteInstance())->
489       increment_active_view_count();
490
491   EXPECT_FALSE(contents()->cross_navigation_pending());
492   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
493   EXPECT_EQ(url, contents()->GetLastCommittedURL());
494   EXPECT_EQ(url, contents()->GetVisibleURL());
495
496   // Navigate to new site
497   const GURL url2("http://www.yahoo.com");
498   controller().LoadURL(
499       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
500   EXPECT_TRUE(contents()->cross_navigation_pending());
501   EXPECT_EQ(url, contents()->GetLastCommittedURL());
502   EXPECT_EQ(url2, contents()->GetVisibleURL());
503   TestRenderViewHost* pending_rvh =
504       static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
505   int pending_rvh_delete_count = 0;
506   pending_rvh->set_delete_counter(&pending_rvh_delete_count);
507   RenderFrameHostImpl* pending_rfh = contents()->GetFrameTree()->root()->
508       render_manager()->pending_frame_host();
509
510   // Navigations should be suspended in pending_rvh until BeforeUnloadACK.
511   EXPECT_TRUE(pending_rvh->are_navigations_suspended());
512   orig_rvh->SendBeforeUnloadACK(true);
513   EXPECT_FALSE(pending_rvh->are_navigations_suspended());
514
515   // DidNavigate from the pending page
516   contents()->TestDidNavigate(
517       pending_rvh, 1, url2, PAGE_TRANSITION_TYPED);
518   SiteInstance* instance2 = contents()->GetSiteInstance();
519
520   // Keep the number of active views in pending_rvh's SiteInstance
521   // non-zero so that orig_rvh doesn't get deleted when it gets
522   // swapped out.
523   static_cast<SiteInstanceImpl*>(pending_rvh->GetSiteInstance())->
524       increment_active_view_count();
525
526   EXPECT_FALSE(contents()->cross_navigation_pending());
527   EXPECT_EQ(pending_rvh, contents()->GetRenderViewHost());
528   EXPECT_EQ(url2, contents()->GetLastCommittedURL());
529   EXPECT_EQ(url2, contents()->GetVisibleURL());
530   EXPECT_NE(instance1, instance2);
531   EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
532   // We keep the original RFH around, swapped out.
533   EXPECT_TRUE(contents()->GetRenderManagerForTesting()->IsOnSwappedOutList(
534       orig_rfh));
535   EXPECT_EQ(orig_rvh_delete_count, 0);
536
537   // Going back should switch SiteInstances again.  The first SiteInstance is
538   // stored in the NavigationEntry, so it should be the same as at the start.
539   // We should use the same RVH as before, swapping it back in.
540   controller().GoBack();
541   TestRenderViewHost* goback_rvh =
542       static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
543   EXPECT_EQ(orig_rvh, goback_rvh);
544   EXPECT_TRUE(contents()->cross_navigation_pending());
545
546   // Navigations should be suspended in goback_rvh until BeforeUnloadACK.
547   EXPECT_TRUE(goback_rvh->are_navigations_suspended());
548   pending_rvh->SendBeforeUnloadACK(true);
549   EXPECT_FALSE(goback_rvh->are_navigations_suspended());
550
551   // DidNavigate from the back action
552   contents()->TestDidNavigate(
553       goback_rvh, 1, url2, PAGE_TRANSITION_TYPED);
554   EXPECT_FALSE(contents()->cross_navigation_pending());
555   EXPECT_EQ(goback_rvh, contents()->GetRenderViewHost());
556   EXPECT_EQ(instance1, contents()->GetSiteInstance());
557   // The pending RFH should now be swapped out, not deleted.
558   EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
559       IsOnSwappedOutList(pending_rfh));
560   EXPECT_EQ(pending_rvh_delete_count, 0);
561   pending_rvh->OnSwappedOut(false);
562
563   // Close contents and ensure RVHs are deleted.
564   DeleteContents();
565   EXPECT_EQ(orig_rvh_delete_count, 1);
566   EXPECT_EQ(pending_rvh_delete_count, 1);
567 }
568
569 // Test that navigating across a site boundary after a crash creates a new
570 // RVH without requiring a cross-site transition (i.e., PENDING state).
571 TEST_F(WebContentsImplTest, CrossSiteBoundariesAfterCrash) {
572   TestRenderViewHost* orig_rvh = test_rvh();
573   int orig_rvh_delete_count = 0;
574   orig_rvh->set_delete_counter(&orig_rvh_delete_count);
575   SiteInstance* instance1 = contents()->GetSiteInstance();
576
577   // Navigate to URL.  First URL should use first RenderViewHost.
578   const GURL url("http://www.google.com");
579   controller().LoadURL(
580       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
581   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
582
583   EXPECT_FALSE(contents()->cross_navigation_pending());
584   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
585
586   // Crash the renderer.
587   orig_rvh->set_render_view_created(false);
588
589   // Navigate to new site.  We should not go into PENDING.
590   const GURL url2("http://www.yahoo.com");
591   controller().LoadURL(
592       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
593   RenderViewHost* new_rvh = rvh();
594   EXPECT_FALSE(contents()->cross_navigation_pending());
595   EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
596   EXPECT_NE(orig_rvh, new_rvh);
597   EXPECT_EQ(orig_rvh_delete_count, 1);
598
599   // DidNavigate from the new page
600   contents()->TestDidNavigate(new_rvh, 1, url2, PAGE_TRANSITION_TYPED);
601   SiteInstance* instance2 = contents()->GetSiteInstance();
602
603   EXPECT_FALSE(contents()->cross_navigation_pending());
604   EXPECT_EQ(new_rvh, rvh());
605   EXPECT_NE(instance1, instance2);
606   EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
607
608   // Close contents and ensure RVHs are deleted.
609   DeleteContents();
610   EXPECT_EQ(orig_rvh_delete_count, 1);
611 }
612
613 // Test that opening a new contents in the same SiteInstance and then navigating
614 // both contentses to a new site will place both contentses in a single
615 // SiteInstance.
616 TEST_F(WebContentsImplTest, NavigateTwoTabsCrossSite) {
617   TestRenderViewHost* orig_rvh = test_rvh();
618   SiteInstance* instance1 = contents()->GetSiteInstance();
619
620   // Navigate to URL.  First URL should use first RenderViewHost.
621   const GURL url("http://www.google.com");
622   controller().LoadURL(
623       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
624   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
625
626   // Open a new contents with the same SiteInstance, navigated to the same site.
627   scoped_ptr<TestWebContents> contents2(
628       TestWebContents::Create(browser_context(), instance1));
629   contents2->GetController().LoadURL(url, Referrer(),
630                                     PAGE_TRANSITION_TYPED,
631                                     std::string());
632   // Need this page id to be 2 since the site instance is the same (which is the
633   // scope of page IDs) and we want to consider this a new page.
634   contents2->TestDidNavigate(
635       contents2->GetRenderViewHost(), 2, url, PAGE_TRANSITION_TYPED);
636
637   // Navigate first contents to a new site.
638   const GURL url2a("http://www.yahoo.com");
639   controller().LoadURL(
640       url2a, Referrer(), PAGE_TRANSITION_TYPED, std::string());
641   orig_rvh->SendBeforeUnloadACK(true);
642   TestRenderViewHost* pending_rvh_a =
643       static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
644   contents()->TestDidNavigate(
645       pending_rvh_a, 1, url2a, PAGE_TRANSITION_TYPED);
646   SiteInstance* instance2a = contents()->GetSiteInstance();
647   EXPECT_NE(instance1, instance2a);
648
649   // Navigate second contents to the same site as the first tab.
650   const GURL url2b("http://mail.yahoo.com");
651   contents2->GetController().LoadURL(url2b, Referrer(),
652                                     PAGE_TRANSITION_TYPED,
653                                     std::string());
654   TestRenderViewHost* rvh2 =
655       static_cast<TestRenderViewHost*>(contents2->GetRenderViewHost());
656   rvh2->SendBeforeUnloadACK(true);
657   TestRenderViewHost* pending_rvh_b =
658       static_cast<TestRenderViewHost*>(contents2->GetPendingRenderViewHost());
659   EXPECT_TRUE(pending_rvh_b != NULL);
660   EXPECT_TRUE(contents2->cross_navigation_pending());
661
662   // NOTE(creis): We used to be in danger of showing a crash page here if the
663   // second contents hadn't navigated somewhere first (bug 1145430).  That case
664   // is now covered by the CrossSiteBoundariesAfterCrash test.
665   contents2->TestDidNavigate(
666       pending_rvh_b, 2, url2b, PAGE_TRANSITION_TYPED);
667   SiteInstance* instance2b = contents2->GetSiteInstance();
668   EXPECT_NE(instance1, instance2b);
669
670   // Both contentses should now be in the same SiteInstance.
671   EXPECT_EQ(instance2a, instance2b);
672 }
673
674 TEST_F(WebContentsImplTest, NavigateDoesNotUseUpSiteInstance) {
675   WebContentsImplTestBrowserClient browser_client;
676   SetBrowserClientForTesting(&browser_client);
677
678   TestRenderViewHost* orig_rvh = test_rvh();
679   RenderFrameHostImpl* orig_rfh =
680       contents()->GetFrameTree()->root()->current_frame_host();
681   int orig_rvh_delete_count = 0;
682   orig_rvh->set_delete_counter(&orig_rvh_delete_count);
683   SiteInstanceImpl* orig_instance =
684       static_cast<SiteInstanceImpl*>(contents()->GetSiteInstance());
685
686   browser_client.set_assign_site_for_url(false);
687   // Navigate to an URL that will not assign a new SiteInstance.
688   const GURL native_url("non-site-url://stuffandthings");
689   controller().LoadURL(
690       native_url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
691   contents()->TestDidNavigate(orig_rvh, 1, native_url, PAGE_TRANSITION_TYPED);
692
693   EXPECT_FALSE(contents()->cross_navigation_pending());
694   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
695   EXPECT_EQ(native_url, contents()->GetLastCommittedURL());
696   EXPECT_EQ(native_url, contents()->GetVisibleURL());
697   EXPECT_EQ(orig_instance, contents()->GetSiteInstance());
698   EXPECT_EQ(GURL(), contents()->GetSiteInstance()->GetSiteURL());
699   EXPECT_FALSE(orig_instance->HasSite());
700
701   browser_client.set_assign_site_for_url(true);
702   // Navigate to new site (should keep same site instance).
703   const GURL url("http://www.google.com");
704   controller().LoadURL(
705       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
706   EXPECT_FALSE(contents()->cross_navigation_pending());
707   EXPECT_EQ(native_url, contents()->GetLastCommittedURL());
708   EXPECT_EQ(url, contents()->GetVisibleURL());
709   EXPECT_FALSE(contents()->GetPendingRenderViewHost());
710   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
711
712   // Keep the number of active views in orig_rvh's SiteInstance
713   // non-zero so that orig_rvh doesn't get deleted when it gets
714   // swapped out.
715   static_cast<SiteInstanceImpl*>(orig_rvh->GetSiteInstance())->
716       increment_active_view_count();
717
718   EXPECT_EQ(orig_instance, contents()->GetSiteInstance());
719   EXPECT_TRUE(
720       contents()->GetSiteInstance()->GetSiteURL().DomainIs("google.com"));
721   EXPECT_EQ(url, contents()->GetLastCommittedURL());
722
723   // Navigate to another new site (should create a new site instance).
724   const GURL url2("http://www.yahoo.com");
725   controller().LoadURL(
726       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
727   EXPECT_TRUE(contents()->cross_navigation_pending());
728   EXPECT_EQ(url, contents()->GetLastCommittedURL());
729   EXPECT_EQ(url2, contents()->GetVisibleURL());
730   TestRenderViewHost* pending_rvh =
731       static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
732   int pending_rvh_delete_count = 0;
733   pending_rvh->set_delete_counter(&pending_rvh_delete_count);
734
735   // Navigations should be suspended in pending_rvh until BeforeUnloadACK.
736   EXPECT_TRUE(pending_rvh->are_navigations_suspended());
737   orig_rvh->SendBeforeUnloadACK(true);
738   EXPECT_FALSE(pending_rvh->are_navigations_suspended());
739
740   // DidNavigate from the pending page.
741   contents()->TestDidNavigate(
742       pending_rvh, 1, url2, PAGE_TRANSITION_TYPED);
743   SiteInstance* new_instance = contents()->GetSiteInstance();
744
745   EXPECT_FALSE(contents()->cross_navigation_pending());
746   EXPECT_EQ(pending_rvh, contents()->GetRenderViewHost());
747   EXPECT_EQ(url2, contents()->GetLastCommittedURL());
748   EXPECT_EQ(url2, contents()->GetVisibleURL());
749   EXPECT_NE(new_instance, orig_instance);
750   EXPECT_FALSE(contents()->GetPendingRenderViewHost());
751   // We keep the original RFH around, swapped out.
752   EXPECT_TRUE(contents()->GetRenderManagerForTesting()->IsOnSwappedOutList(
753       orig_rfh));
754   EXPECT_EQ(orig_rvh_delete_count, 0);
755   orig_rvh->OnSwappedOut(false);
756
757   // Close contents and ensure RVHs are deleted.
758   DeleteContents();
759   EXPECT_EQ(orig_rvh_delete_count, 1);
760   EXPECT_EQ(pending_rvh_delete_count, 1);
761 }
762
763 // Test that we can find an opener RVH even if it's pending.
764 // http://crbug.com/176252.
765 TEST_F(WebContentsImplTest, FindOpenerRVHWhenPending) {
766   TestRenderViewHost* orig_rvh = test_rvh();
767
768   // Navigate to a URL.
769   const GURL url("http://www.google.com");
770   controller().LoadURL(
771       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
772   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
773
774   // Start to navigate first tab to a new site, so that it has a pending RVH.
775   const GURL url2("http://www.yahoo.com");
776   controller().LoadURL(
777       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
778   orig_rvh->SendBeforeUnloadACK(true);
779   TestRenderViewHost* pending_rvh =
780       static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
781
782   // While it is still pending, simulate opening a new tab with the first tab
783   // as its opener.  This will call WebContentsImpl::CreateOpenerRenderViews
784   // on the opener to ensure that an RVH exists.
785   int opener_routing_id = contents()->CreateOpenerRenderViews(
786       pending_rvh->GetSiteInstance());
787
788   // We should find the pending RVH and not create a new one.
789   EXPECT_EQ(pending_rvh->GetRoutingID(), opener_routing_id);
790 }
791
792 // Tests that WebContentsImpl uses the current URL, not the SiteInstance's site,
793 // to determine whether a navigation is cross-site.
794 TEST_F(WebContentsImplTest, CrossSiteComparesAgainstCurrentPage) {
795   RenderViewHost* orig_rvh = rvh();
796   SiteInstance* instance1 = contents()->GetSiteInstance();
797
798   // Navigate to URL.
799   const GURL url("http://www.google.com");
800   controller().LoadURL(
801       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
802   contents()->TestDidNavigate(
803       orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
804
805   // Open a related contents to a second site.
806   scoped_ptr<TestWebContents> contents2(
807       TestWebContents::Create(browser_context(), instance1));
808   const GURL url2("http://www.yahoo.com");
809   contents2->GetController().LoadURL(url2, Referrer(),
810                                     PAGE_TRANSITION_TYPED,
811                                     std::string());
812   // The first RVH in contents2 isn't live yet, so we shortcut the cross site
813   // pending.
814   TestRenderViewHost* rvh2 = static_cast<TestRenderViewHost*>(
815       contents2->GetRenderViewHost());
816   EXPECT_FALSE(contents2->cross_navigation_pending());
817   contents2->TestDidNavigate(rvh2, 2, url2, PAGE_TRANSITION_TYPED);
818   SiteInstance* instance2 = contents2->GetSiteInstance();
819   EXPECT_NE(instance1, instance2);
820   EXPECT_FALSE(contents2->cross_navigation_pending());
821
822   // Simulate a link click in first contents to second site.  Doesn't switch
823   // SiteInstances, because we don't intercept WebKit navigations.
824   contents()->TestDidNavigate(
825       orig_rvh, 2, url2, PAGE_TRANSITION_TYPED);
826   SiteInstance* instance3 = contents()->GetSiteInstance();
827   EXPECT_EQ(instance1, instance3);
828   EXPECT_FALSE(contents()->cross_navigation_pending());
829
830   // Navigate to the new site.  Doesn't switch SiteInstancees, because we
831   // compare against the current URL, not the SiteInstance's site.
832   const GURL url3("http://mail.yahoo.com");
833   controller().LoadURL(
834       url3, Referrer(), PAGE_TRANSITION_TYPED, std::string());
835   EXPECT_FALSE(contents()->cross_navigation_pending());
836   contents()->TestDidNavigate(
837       orig_rvh, 3, url3, PAGE_TRANSITION_TYPED);
838   SiteInstance* instance4 = contents()->GetSiteInstance();
839   EXPECT_EQ(instance1, instance4);
840 }
841
842 // Test that the onbeforeunload and onunload handlers run when navigating
843 // across site boundaries.
844 TEST_F(WebContentsImplTest, CrossSiteUnloadHandlers) {
845   TestRenderViewHost* orig_rvh = test_rvh();
846   SiteInstance* instance1 = contents()->GetSiteInstance();
847
848   // Navigate to URL.  First URL should use first RenderViewHost.
849   const GURL url("http://www.google.com");
850   controller().LoadURL(
851       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
852   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
853   EXPECT_FALSE(contents()->cross_navigation_pending());
854   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
855
856   // Navigate to new site, but simulate an onbeforeunload denial.
857   const GURL url2("http://www.yahoo.com");
858   controller().LoadURL(
859       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
860   EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
861   base::TimeTicks now = base::TimeTicks::Now();
862   orig_rvh->GetMainFrame()->OnMessageReceived(
863       FrameHostMsg_BeforeUnload_ACK(0, false, now, now));
864   EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
865   EXPECT_FALSE(contents()->cross_navigation_pending());
866   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
867
868   // Navigate again, but simulate an onbeforeunload approval.
869   controller().LoadURL(
870       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
871   EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
872   now = base::TimeTicks::Now();
873   orig_rvh->GetMainFrame()->OnMessageReceived(
874       FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
875   EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
876   EXPECT_TRUE(contents()->cross_navigation_pending());
877   TestRenderViewHost* pending_rvh = static_cast<TestRenderViewHost*>(
878       contents()->GetPendingRenderViewHost());
879
880   // We won't hear DidNavigate until the onunload handler has finished running.
881
882   // DidNavigate from the pending page.
883   contents()->TestDidNavigate(
884       pending_rvh, 1, url2, PAGE_TRANSITION_TYPED);
885   SiteInstance* instance2 = contents()->GetSiteInstance();
886   EXPECT_FALSE(contents()->cross_navigation_pending());
887   EXPECT_EQ(pending_rvh, rvh());
888   EXPECT_NE(instance1, instance2);
889   EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
890 }
891
892 // Test that during a slow cross-site navigation, the original renderer can
893 // navigate to a different URL and have it displayed, canceling the slow
894 // navigation.
895 TEST_F(WebContentsImplTest, CrossSiteNavigationPreempted) {
896   TestRenderViewHost* orig_rvh = test_rvh();
897   SiteInstance* instance1 = contents()->GetSiteInstance();
898
899   // Navigate to URL.  First URL should use first RenderViewHost.
900   const GURL url("http://www.google.com");
901   controller().LoadURL(
902       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
903   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
904   EXPECT_FALSE(contents()->cross_navigation_pending());
905   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
906
907   // Navigate to new site, simulating an onbeforeunload approval.
908   const GURL url2("http://www.yahoo.com");
909   controller().LoadURL(
910       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
911   EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
912   base::TimeTicks now = base::TimeTicks::Now();
913   orig_rvh->GetMainFrame()->OnMessageReceived(
914       FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
915   EXPECT_TRUE(contents()->cross_navigation_pending());
916
917   // Suppose the original renderer navigates before the new one is ready.
918   orig_rvh->SendNavigate(2, GURL("http://www.google.com/foo"));
919
920   // Verify that the pending navigation is cancelled.
921   EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
922   SiteInstance* instance2 = contents()->GetSiteInstance();
923   EXPECT_FALSE(contents()->cross_navigation_pending());
924   EXPECT_EQ(orig_rvh, rvh());
925   EXPECT_EQ(instance1, instance2);
926   EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
927 }
928
929 TEST_F(WebContentsImplTest, CrossSiteNavigationBackPreempted) {
930   // Start with a web ui page, which gets a new RVH with WebUI bindings.
931   const GURL url1("chrome://blah");
932   controller().LoadURL(
933       url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
934   TestRenderViewHost* ntp_rvh = test_rvh();
935   contents()->TestDidNavigate(ntp_rvh, 1, url1, PAGE_TRANSITION_TYPED);
936   NavigationEntry* entry1 = controller().GetLastCommittedEntry();
937   SiteInstance* instance1 = contents()->GetSiteInstance();
938
939   EXPECT_FALSE(contents()->cross_navigation_pending());
940   EXPECT_EQ(ntp_rvh, contents()->GetRenderViewHost());
941   EXPECT_EQ(url1, entry1->GetURL());
942   EXPECT_EQ(instance1,
943             NavigationEntryImpl::FromNavigationEntry(entry1)->site_instance());
944   EXPECT_TRUE(ntp_rvh->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
945
946   // Navigate to new site.
947   const GURL url2("http://www.google.com");
948   controller().LoadURL(
949       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
950   EXPECT_TRUE(contents()->cross_navigation_pending());
951   TestRenderViewHost* google_rvh =
952       static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
953
954   // Simulate beforeunload approval.
955   EXPECT_TRUE(ntp_rvh->is_waiting_for_beforeunload_ack());
956   base::TimeTicks now = base::TimeTicks::Now();
957   ntp_rvh->GetMainFrame()->OnMessageReceived(
958       FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
959
960   // DidNavigate from the pending page.
961   contents()->TestDidNavigate(
962       google_rvh, 1, url2, PAGE_TRANSITION_TYPED);
963   NavigationEntry* entry2 = controller().GetLastCommittedEntry();
964   SiteInstance* instance2 = contents()->GetSiteInstance();
965
966   EXPECT_FALSE(contents()->cross_navigation_pending());
967   EXPECT_EQ(google_rvh, contents()->GetRenderViewHost());
968   EXPECT_NE(instance1, instance2);
969   EXPECT_FALSE(contents()->GetPendingRenderViewHost());
970   EXPECT_EQ(url2, entry2->GetURL());
971   EXPECT_EQ(instance2,
972             NavigationEntryImpl::FromNavigationEntry(entry2)->site_instance());
973   EXPECT_FALSE(google_rvh->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
974
975   // Navigate to third page on same site.
976   const GURL url3("http://news.google.com");
977   controller().LoadURL(
978       url3, Referrer(), PAGE_TRANSITION_TYPED, std::string());
979   EXPECT_FALSE(contents()->cross_navigation_pending());
980   contents()->TestDidNavigate(
981       google_rvh, 2, url3, PAGE_TRANSITION_TYPED);
982   NavigationEntry* entry3 = controller().GetLastCommittedEntry();
983   SiteInstance* instance3 = contents()->GetSiteInstance();
984
985   EXPECT_FALSE(contents()->cross_navigation_pending());
986   EXPECT_EQ(google_rvh, contents()->GetRenderViewHost());
987   EXPECT_EQ(instance2, instance3);
988   EXPECT_FALSE(contents()->GetPendingRenderViewHost());
989   EXPECT_EQ(url3, entry3->GetURL());
990   EXPECT_EQ(instance3,
991             NavigationEntryImpl::FromNavigationEntry(entry3)->site_instance());
992
993   // Go back within the site.
994   controller().GoBack();
995   EXPECT_FALSE(contents()->cross_navigation_pending());
996   EXPECT_EQ(entry2, controller().GetPendingEntry());
997
998   // Before that commits, go back again.
999   controller().GoBack();
1000   EXPECT_TRUE(contents()->cross_navigation_pending());
1001   EXPECT_TRUE(contents()->GetPendingRenderViewHost());
1002   EXPECT_EQ(entry1, controller().GetPendingEntry());
1003
1004   // Simulate beforeunload approval.
1005   EXPECT_TRUE(google_rvh->is_waiting_for_beforeunload_ack());
1006   now = base::TimeTicks::Now();
1007   google_rvh->GetMainFrame()->OnMessageReceived(
1008       FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
1009
1010   // DidNavigate from the first back. This aborts the second back's pending RVH.
1011   contents()->TestDidNavigate(google_rvh, 1, url2, PAGE_TRANSITION_TYPED);
1012
1013   // We should commit this page and forget about the second back.
1014   EXPECT_FALSE(contents()->cross_navigation_pending());
1015   EXPECT_FALSE(controller().GetPendingEntry());
1016   EXPECT_EQ(google_rvh, contents()->GetRenderViewHost());
1017   EXPECT_EQ(url2, controller().GetLastCommittedEntry()->GetURL());
1018
1019   // We should not have corrupted the NTP entry.
1020   EXPECT_EQ(instance3,
1021             NavigationEntryImpl::FromNavigationEntry(entry3)->site_instance());
1022   EXPECT_EQ(instance2,
1023             NavigationEntryImpl::FromNavigationEntry(entry2)->site_instance());
1024   EXPECT_EQ(instance1,
1025             NavigationEntryImpl::FromNavigationEntry(entry1)->site_instance());
1026   EXPECT_EQ(url1, entry1->GetURL());
1027 }
1028
1029 // Test that during a slow cross-site navigation, a sub-frame navigation in the
1030 // original renderer will not cancel the slow navigation (bug 42029).
1031 TEST_F(WebContentsImplTest, CrossSiteNavigationNotPreemptedByFrame) {
1032   TestRenderViewHost* orig_rvh = test_rvh();
1033
1034   // Navigate to URL.  First URL should use first RenderViewHost.
1035   const GURL url("http://www.google.com");
1036   controller().LoadURL(
1037       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1038   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
1039   EXPECT_FALSE(contents()->cross_navigation_pending());
1040   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
1041
1042   // Start navigating to new site.
1043   const GURL url2("http://www.yahoo.com");
1044   controller().LoadURL(
1045       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1046
1047   // Simulate a sub-frame navigation arriving and ensure the RVH is still
1048   // waiting for a before unload response.
1049   orig_rvh->SendNavigateWithTransition(1, GURL("http://google.com/frame"),
1050                                        PAGE_TRANSITION_AUTO_SUBFRAME);
1051   EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
1052
1053   // Now simulate the onbeforeunload approval and verify the navigation is
1054   // not canceled.
1055   base::TimeTicks now = base::TimeTicks::Now();
1056   orig_rvh->GetMainFrame()->OnMessageReceived(
1057       FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
1058   EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
1059   EXPECT_TRUE(contents()->cross_navigation_pending());
1060 }
1061
1062 // Test that a cross-site navigation is not preempted if the previous
1063 // renderer sends a FrameNavigate message just before being told to stop.
1064 // We should only preempt the cross-site navigation if the previous renderer
1065 // has started a new navigation.  See http://crbug.com/79176.
1066 TEST_F(WebContentsImplTest, CrossSiteNotPreemptedDuringBeforeUnload) {
1067   // Navigate to NTP URL.
1068   const GURL url("chrome://blah");
1069   controller().LoadURL(
1070       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1071   TestRenderViewHost* orig_rvh = test_rvh();
1072   EXPECT_FALSE(contents()->cross_navigation_pending());
1073
1074   // Navigate to new site, with the beforeunload request in flight.
1075   const GURL url2("http://www.yahoo.com");
1076   controller().LoadURL(
1077       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1078   TestRenderViewHost* pending_rvh =
1079       static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
1080   EXPECT_TRUE(contents()->cross_navigation_pending());
1081   EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
1082
1083   // Suppose the first navigation tries to commit now, with a
1084   // ViewMsg_Stop in flight.  This should not cancel the pending navigation,
1085   // but it should act as if the beforeunload ack arrived.
1086   orig_rvh->SendNavigate(1, GURL("chrome://blah"));
1087   EXPECT_TRUE(contents()->cross_navigation_pending());
1088   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
1089   EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
1090
1091   // The pending navigation should be able to commit successfully.
1092   contents()->TestDidNavigate(pending_rvh, 1, url2, PAGE_TRANSITION_TYPED);
1093   EXPECT_FALSE(contents()->cross_navigation_pending());
1094   EXPECT_EQ(pending_rvh, contents()->GetRenderViewHost());
1095 }
1096
1097 // Test that the original renderer cannot preempt a cross-site navigation once
1098 // the unload request has been made.  At this point, the cross-site navigation
1099 // is almost ready to be displayed, and the original renderer is only given a
1100 // short chance to run an unload handler.  Prevents regression of bug 23942.
1101 TEST_F(WebContentsImplTest, CrossSiteCantPreemptAfterUnload) {
1102   TestRenderViewHost* orig_rvh = test_rvh();
1103   SiteInstance* instance1 = contents()->GetSiteInstance();
1104
1105   // Navigate to URL.  First URL should use first RenderViewHost.
1106   const GURL url("http://www.google.com");
1107   controller().LoadURL(
1108       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1109   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
1110   EXPECT_FALSE(contents()->cross_navigation_pending());
1111   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
1112
1113   // Navigate to new site, simulating an onbeforeunload approval.
1114   const GURL url2("http://www.yahoo.com");
1115   controller().LoadURL(
1116       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1117   base::TimeTicks now = base::TimeTicks::Now();
1118   orig_rvh->GetMainFrame()->OnMessageReceived(
1119       FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
1120   EXPECT_TRUE(contents()->cross_navigation_pending());
1121   TestRenderViewHost* pending_rvh = static_cast<TestRenderViewHost*>(
1122       contents()->GetPendingRenderViewHost());
1123
1124   // Simulate the pending renderer's response, which leads to an unload request
1125   // being sent to orig_rvh.
1126   std::vector<GURL> url_chain;
1127   url_chain.push_back(GURL());
1128   contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
1129       contents()->GetRenderManagerForTesting()->pending_frame_host(),
1130       GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
1131       url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
1132
1133   // Suppose the original renderer navigates now, while the unload request is in
1134   // flight.  We should ignore it, wait for the unload ack, and let the pending
1135   // request continue.  Otherwise, the contents may close spontaneously or stop
1136   // responding to navigation requests.  (See bug 23942.)
1137   FrameHostMsg_DidCommitProvisionalLoad_Params params1a;
1138   InitNavigateParams(&params1a, 2, GURL("http://www.google.com/foo"),
1139                      PAGE_TRANSITION_TYPED);
1140   orig_rvh->SendNavigate(2, GURL("http://www.google.com/foo"));
1141
1142   // Verify that the pending navigation is still in progress.
1143   EXPECT_TRUE(contents()->cross_navigation_pending());
1144   EXPECT_TRUE(contents()->GetPendingRenderViewHost() != NULL);
1145
1146   // DidNavigate from the pending page should commit it.
1147   contents()->TestDidNavigate(
1148       pending_rvh, 1, url2, PAGE_TRANSITION_TYPED);
1149   SiteInstance* instance2 = contents()->GetSiteInstance();
1150   EXPECT_FALSE(contents()->cross_navigation_pending());
1151   EXPECT_EQ(pending_rvh, rvh());
1152   EXPECT_NE(instance1, instance2);
1153   EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
1154 }
1155
1156 // Test that a cross-site navigation that doesn't commit after the unload
1157 // handler doesn't leave the contents in a stuck state.  http://crbug.com/88562
1158 TEST_F(WebContentsImplTest, CrossSiteNavigationCanceled) {
1159   TestRenderViewHost* orig_rvh = test_rvh();
1160   SiteInstance* instance1 = contents()->GetSiteInstance();
1161
1162   // Navigate to URL.  First URL should use first RenderViewHost.
1163   const GURL url("http://www.google.com");
1164   controller().LoadURL(
1165       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1166   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
1167   EXPECT_FALSE(contents()->cross_navigation_pending());
1168   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
1169
1170   // Navigate to new site, simulating an onbeforeunload approval.
1171   const GURL url2("http://www.yahoo.com");
1172   controller().LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1173   EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
1174   base::TimeTicks now = base::TimeTicks::Now();
1175   orig_rvh->GetMainFrame()->OnMessageReceived(
1176       FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
1177   EXPECT_TRUE(contents()->cross_navigation_pending());
1178
1179   // Simulate swap out message when the response arrives.
1180   orig_rvh->OnSwappedOut(false);
1181
1182   // Suppose the navigation doesn't get a chance to commit, and the user
1183   // navigates in the current RVH's SiteInstance.
1184   controller().LoadURL(url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1185
1186   // Verify that the pending navigation is cancelled and the renderer is no
1187   // longer swapped out.
1188   EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
1189   SiteInstance* instance2 = contents()->GetSiteInstance();
1190   EXPECT_FALSE(contents()->cross_navigation_pending());
1191   EXPECT_EQ(orig_rvh, rvh());
1192   EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, orig_rvh->rvh_state());
1193   EXPECT_EQ(instance1, instance2);
1194   EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
1195 }
1196
1197 // Test that NavigationEntries have the correct page state after going
1198 // forward and back.  Prevents regression for bug 1116137.
1199 TEST_F(WebContentsImplTest, NavigationEntryContentState) {
1200   TestRenderViewHost* orig_rvh = test_rvh();
1201
1202   // Navigate to URL.  There should be no committed entry yet.
1203   const GURL url("http://www.google.com");
1204   controller().LoadURL(url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1205   NavigationEntry* entry = controller().GetLastCommittedEntry();
1206   EXPECT_TRUE(entry == NULL);
1207
1208   // Committed entry should have page state after DidNavigate.
1209   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
1210   entry = controller().GetLastCommittedEntry();
1211   EXPECT_TRUE(entry->GetPageState().IsValid());
1212
1213   // Navigate to same site.
1214   const GURL url2("http://images.google.com");
1215   controller().LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1216   entry = controller().GetLastCommittedEntry();
1217   EXPECT_TRUE(entry->GetPageState().IsValid());
1218
1219   // Committed entry should have page state after DidNavigate.
1220   contents()->TestDidNavigate(orig_rvh, 2, url2, PAGE_TRANSITION_TYPED);
1221   entry = controller().GetLastCommittedEntry();
1222   EXPECT_TRUE(entry->GetPageState().IsValid());
1223
1224   // Now go back.  Committed entry should still have page state.
1225   controller().GoBack();
1226   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
1227   entry = controller().GetLastCommittedEntry();
1228   EXPECT_TRUE(entry->GetPageState().IsValid());
1229 }
1230
1231 // Test that NavigationEntries have the correct page state and SiteInstance
1232 // state after opening a new window to about:blank.  Prevents regression for
1233 // bugs b/1116137 and http://crbug.com/111975.
1234 TEST_F(WebContentsImplTest, NavigationEntryContentStateNewWindow) {
1235   TestRenderViewHost* orig_rvh = test_rvh();
1236
1237   // When opening a new window, it is navigated to about:blank internally.
1238   // Currently, this results in two DidNavigate events.
1239   const GURL url(url::kAboutBlankURL);
1240   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
1241   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
1242
1243   // Should have a page state here.
1244   NavigationEntry* entry = controller().GetLastCommittedEntry();
1245   EXPECT_TRUE(entry->GetPageState().IsValid());
1246
1247   // The SiteInstance should be available for other navigations to use.
1248   NavigationEntryImpl* entry_impl =
1249       NavigationEntryImpl::FromNavigationEntry(entry);
1250   EXPECT_FALSE(entry_impl->site_instance()->HasSite());
1251   int32 site_instance_id = entry_impl->site_instance()->GetId();
1252
1253   // Navigating to a normal page should not cause a process swap.
1254   const GURL new_url("http://www.google.com");
1255   controller().LoadURL(new_url, Referrer(),
1256                        PAGE_TRANSITION_TYPED, std::string());
1257   EXPECT_FALSE(contents()->cross_navigation_pending());
1258   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
1259   contents()->TestDidNavigate(orig_rvh, 1, new_url, PAGE_TRANSITION_TYPED);
1260   NavigationEntryImpl* entry_impl2 = NavigationEntryImpl::FromNavigationEntry(
1261       controller().GetLastCommittedEntry());
1262   EXPECT_EQ(site_instance_id, entry_impl2->site_instance()->GetId());
1263   EXPECT_TRUE(entry_impl2->site_instance()->HasSite());
1264 }
1265
1266 // Tests that fullscreen is exited throughout the object hierarchy when
1267 // navigating to a new page.
1268 TEST_F(WebContentsImplTest, NavigationExitsFullscreen) {
1269   FakeFullscreenDelegate fake_delegate;
1270   contents()->SetDelegate(&fake_delegate);
1271   TestRenderViewHost* orig_rvh = test_rvh();
1272
1273   // Navigate to a site.
1274   const GURL url("http://www.google.com");
1275   controller().LoadURL(
1276       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1277   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
1278   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
1279
1280   // Toggle fullscreen mode on (as if initiated via IPC from renderer).
1281   EXPECT_FALSE(orig_rvh->IsFullscreen());
1282   EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
1283   EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1284   orig_rvh->OnMessageReceived(
1285       ViewHostMsg_ToggleFullscreen(orig_rvh->GetRoutingID(), true));
1286   EXPECT_TRUE(orig_rvh->IsFullscreen());
1287   EXPECT_TRUE(contents()->IsFullscreenForCurrentTab());
1288   EXPECT_TRUE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1289
1290   // Navigate to a new site.
1291   const GURL url2("http://www.yahoo.com");
1292   controller().LoadURL(
1293       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1294   RenderViewHost* const pending_rvh = contents()->GetPendingRenderViewHost();
1295   contents()->TestDidNavigate(
1296       pending_rvh, 1, url2, PAGE_TRANSITION_TYPED);
1297
1298   // Confirm fullscreen has exited.
1299   EXPECT_FALSE(orig_rvh->IsFullscreen());
1300   EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
1301   EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1302
1303   contents()->SetDelegate(NULL);
1304 }
1305
1306 // Tests that fullscreen is exited throughout the object hierarchy when
1307 // instructing NavigationController to GoBack() or GoForward().
1308 TEST_F(WebContentsImplTest, HistoryNavigationExitsFullscreen) {
1309   FakeFullscreenDelegate fake_delegate;
1310   contents()->SetDelegate(&fake_delegate);
1311   TestRenderViewHost* const orig_rvh = test_rvh();
1312
1313   // Navigate to a site.
1314   const GURL url("http://www.google.com");
1315   controller().LoadURL(url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1316   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
1317   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
1318
1319   // Now, navigate to another page on the same site.
1320   const GURL url2("http://www.google.com/search?q=kittens");
1321   controller().LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1322   EXPECT_FALSE(contents()->cross_navigation_pending());
1323   contents()->TestDidNavigate(orig_rvh, 2, url2, PAGE_TRANSITION_TYPED);
1324   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
1325
1326   // Sanity-check: Confirm we're not starting out in fullscreen mode.
1327   EXPECT_FALSE(orig_rvh->IsFullscreen());
1328   EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
1329   EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1330
1331   for (int i = 0; i < 2; ++i) {
1332     // Toggle fullscreen mode on (as if initiated via IPC from renderer).
1333     orig_rvh->OnMessageReceived(
1334         ViewHostMsg_ToggleFullscreen(orig_rvh->GetRoutingID(), true));
1335     EXPECT_TRUE(orig_rvh->IsFullscreen());
1336     EXPECT_TRUE(contents()->IsFullscreenForCurrentTab());
1337     EXPECT_TRUE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1338
1339     // Navigate backward (or forward).
1340     if (i == 0)
1341       controller().GoBack();
1342     else
1343       controller().GoForward();
1344     EXPECT_FALSE(contents()->cross_navigation_pending());
1345     EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
1346     contents()->TestDidNavigate(
1347         orig_rvh, i + 1, url, PAGE_TRANSITION_FORWARD_BACK);
1348
1349     // Confirm fullscreen has exited.
1350     EXPECT_FALSE(orig_rvh->IsFullscreen());
1351     EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
1352     EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1353   }
1354
1355   contents()->SetDelegate(NULL);
1356 }
1357
1358 TEST_F(WebContentsImplTest, TerminateHidesValidationMessage) {
1359   FakeValidationMessageDelegate fake_delegate;
1360   contents()->SetDelegate(&fake_delegate);
1361   EXPECT_FALSE(fake_delegate.hide_validation_message_was_called());
1362
1363   // Crash the renderer.
1364   test_rvh()->OnMessageReceived(
1365       ViewHostMsg_RenderProcessGone(
1366           0, base::TERMINATION_STATUS_PROCESS_CRASHED, -1));
1367
1368   // Confirm HideValidationMessage was called.
1369   EXPECT_TRUE(fake_delegate.hide_validation_message_was_called());
1370
1371   contents()->SetDelegate(NULL);
1372 }
1373
1374 // Tests that fullscreen is exited throughout the object hierarchy on a renderer
1375 // crash.
1376 TEST_F(WebContentsImplTest, CrashExitsFullscreen) {
1377   FakeFullscreenDelegate fake_delegate;
1378   contents()->SetDelegate(&fake_delegate);
1379
1380   // Navigate to a site.
1381   const GURL url("http://www.google.com");
1382   controller().LoadURL(
1383       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1384   contents()->TestDidNavigate(test_rvh(), 1, url, PAGE_TRANSITION_TYPED);
1385   EXPECT_EQ(test_rvh(), contents()->GetRenderViewHost());
1386
1387   // Toggle fullscreen mode on (as if initiated via IPC from renderer).
1388   EXPECT_FALSE(test_rvh()->IsFullscreen());
1389   EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
1390   EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1391   test_rvh()->OnMessageReceived(
1392       ViewHostMsg_ToggleFullscreen(test_rvh()->GetRoutingID(), true));
1393   EXPECT_TRUE(test_rvh()->IsFullscreen());
1394   EXPECT_TRUE(contents()->IsFullscreenForCurrentTab());
1395   EXPECT_TRUE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1396
1397   // Crash the renderer.
1398   test_rvh()->OnMessageReceived(
1399       ViewHostMsg_RenderProcessGone(
1400           0, base::TERMINATION_STATUS_PROCESS_CRASHED, -1));
1401
1402   // Confirm fullscreen has exited.
1403   EXPECT_FALSE(test_rvh()->IsFullscreen());
1404   EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
1405   EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1406
1407   contents()->SetDelegate(NULL);
1408 }
1409
1410 ////////////////////////////////////////////////////////////////////////////////
1411 // Interstitial Tests
1412 ////////////////////////////////////////////////////////////////////////////////
1413
1414 // Test navigating to a page (with the navigation initiated from the browser,
1415 // as when a URL is typed in the location bar) that shows an interstitial and
1416 // creates a new navigation entry, then hiding it without proceeding.
1417 TEST_F(WebContentsImplTest,
1418        ShowInterstitialFromBrowserWithNewNavigationDontProceed) {
1419   // Navigate to a page.
1420   GURL url1("http://www.google.com");
1421   test_rvh()->SendNavigate(1, url1);
1422   EXPECT_EQ(1, controller().GetEntryCount());
1423
1424   // Initiate a browser navigation that will trigger the interstitial
1425   controller().LoadURL(GURL("http://www.evil.com"), Referrer(),
1426                         PAGE_TRANSITION_TYPED, std::string());
1427
1428   // Show an interstitial.
1429   TestInterstitialPage::InterstitialState state =
1430       TestInterstitialPage::INVALID;
1431   bool deleted = false;
1432   GURL url2("http://interstitial");
1433   TestInterstitialPage* interstitial =
1434       new TestInterstitialPage(contents(), true, url2, &state, &deleted);
1435   TestInterstitialPageStateGuard state_guard(interstitial);
1436   interstitial->Show();
1437   // The interstitial should not show until its navigation has committed.
1438   EXPECT_FALSE(interstitial->is_showing());
1439   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1440   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1441   // Let's commit the interstitial navigation.
1442   interstitial->TestDidNavigate(1, url2);
1443   EXPECT_TRUE(interstitial->is_showing());
1444   EXPECT_TRUE(contents()->ShowingInterstitialPage());
1445   EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1446   NavigationEntry* entry = controller().GetVisibleEntry();
1447   ASSERT_TRUE(entry != NULL);
1448   EXPECT_TRUE(entry->GetURL() == url2);
1449
1450   // Now don't proceed.
1451   interstitial->DontProceed();
1452   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1453   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1454   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1455   entry = controller().GetVisibleEntry();
1456   ASSERT_TRUE(entry != NULL);
1457   EXPECT_TRUE(entry->GetURL() == url1);
1458   EXPECT_EQ(1, controller().GetEntryCount());
1459
1460   RunAllPendingInMessageLoop();
1461   EXPECT_TRUE(deleted);
1462 }
1463
1464 // Test navigating to a page (with the navigation initiated from the renderer,
1465 // as when clicking on a link in the page) that shows an interstitial and
1466 // creates a new navigation entry, then hiding it without proceeding.
1467 TEST_F(WebContentsImplTest,
1468        ShowInterstitiaFromRendererlWithNewNavigationDontProceed) {
1469   // Navigate to a page.
1470   GURL url1("http://www.google.com");
1471   test_rvh()->SendNavigate(1, url1);
1472   EXPECT_EQ(1, controller().GetEntryCount());
1473
1474   // Show an interstitial (no pending entry, the interstitial would have been
1475   // triggered by clicking on a link).
1476   TestInterstitialPage::InterstitialState state =
1477       TestInterstitialPage::INVALID;
1478   bool deleted = false;
1479   GURL url2("http://interstitial");
1480   TestInterstitialPage* interstitial =
1481       new TestInterstitialPage(contents(), true, url2, &state, &deleted);
1482   TestInterstitialPageStateGuard state_guard(interstitial);
1483   interstitial->Show();
1484   // The interstitial should not show until its navigation has committed.
1485   EXPECT_FALSE(interstitial->is_showing());
1486   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1487   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1488   // Let's commit the interstitial navigation.
1489   interstitial->TestDidNavigate(1, url2);
1490   EXPECT_TRUE(interstitial->is_showing());
1491   EXPECT_TRUE(contents()->ShowingInterstitialPage());
1492   EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1493   NavigationEntry* entry = controller().GetVisibleEntry();
1494   ASSERT_TRUE(entry != NULL);
1495   EXPECT_TRUE(entry->GetURL() == url2);
1496
1497   // Now don't proceed.
1498   interstitial->DontProceed();
1499   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1500   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1501   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1502   entry = controller().GetVisibleEntry();
1503   ASSERT_TRUE(entry != NULL);
1504   EXPECT_TRUE(entry->GetURL() == url1);
1505   EXPECT_EQ(1, controller().GetEntryCount());
1506
1507   RunAllPendingInMessageLoop();
1508   EXPECT_TRUE(deleted);
1509 }
1510
1511 // Test navigating to a page that shows an interstitial without creating a new
1512 // navigation entry (this happens when the interstitial is triggered by a
1513 // sub-resource in the page), then hiding it without proceeding.
1514 TEST_F(WebContentsImplTest, ShowInterstitialNoNewNavigationDontProceed) {
1515   // Navigate to a page.
1516   GURL url1("http://www.google.com");
1517   test_rvh()->SendNavigate(1, url1);
1518   EXPECT_EQ(1, controller().GetEntryCount());
1519
1520   // Show an interstitial.
1521   TestInterstitialPage::InterstitialState state =
1522       TestInterstitialPage::INVALID;
1523   bool deleted = false;
1524   GURL url2("http://interstitial");
1525   TestInterstitialPage* interstitial =
1526       new TestInterstitialPage(contents(), false, url2, &state, &deleted);
1527   TestInterstitialPageStateGuard state_guard(interstitial);
1528   interstitial->Show();
1529   // The interstitial should not show until its navigation has committed.
1530   EXPECT_FALSE(interstitial->is_showing());
1531   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1532   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1533   // Let's commit the interstitial navigation.
1534   interstitial->TestDidNavigate(1, url2);
1535   EXPECT_TRUE(interstitial->is_showing());
1536   EXPECT_TRUE(contents()->ShowingInterstitialPage());
1537   EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1538   NavigationEntry* entry = controller().GetVisibleEntry();
1539   ASSERT_TRUE(entry != NULL);
1540   // The URL specified to the interstitial should have been ignored.
1541   EXPECT_TRUE(entry->GetURL() == url1);
1542
1543   // Now don't proceed.
1544   interstitial->DontProceed();
1545   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1546   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1547   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1548   entry = controller().GetVisibleEntry();
1549   ASSERT_TRUE(entry != NULL);
1550   EXPECT_TRUE(entry->GetURL() == url1);
1551   EXPECT_EQ(1, controller().GetEntryCount());
1552
1553   RunAllPendingInMessageLoop();
1554   EXPECT_TRUE(deleted);
1555 }
1556
1557 // Test navigating to a page (with the navigation initiated from the browser,
1558 // as when a URL is typed in the location bar) that shows an interstitial and
1559 // creates a new navigation entry, then proceeding.
1560 TEST_F(WebContentsImplTest,
1561        ShowInterstitialFromBrowserNewNavigationProceed) {
1562   // Navigate to a page.
1563   GURL url1("http://www.google.com");
1564   test_rvh()->SendNavigate(1, url1);
1565   EXPECT_EQ(1, controller().GetEntryCount());
1566
1567   // Initiate a browser navigation that will trigger the interstitial
1568   controller().LoadURL(GURL("http://www.evil.com"), Referrer(),
1569                         PAGE_TRANSITION_TYPED, std::string());
1570
1571   // Show an interstitial.
1572   TestInterstitialPage::InterstitialState state =
1573       TestInterstitialPage::INVALID;
1574   bool deleted = false;
1575   GURL url2("http://interstitial");
1576   TestInterstitialPage* interstitial =
1577       new TestInterstitialPage(contents(), true, url2, &state, &deleted);
1578   TestInterstitialPageStateGuard state_guard(interstitial);
1579   interstitial->Show();
1580   // The interstitial should not show until its navigation has committed.
1581   EXPECT_FALSE(interstitial->is_showing());
1582   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1583   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1584   // Let's commit the interstitial navigation.
1585   interstitial->TestDidNavigate(1, url2);
1586   EXPECT_TRUE(interstitial->is_showing());
1587   EXPECT_TRUE(contents()->ShowingInterstitialPage());
1588   EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1589   NavigationEntry* entry = controller().GetVisibleEntry();
1590   ASSERT_TRUE(entry != NULL);
1591   EXPECT_TRUE(entry->GetURL() == url2);
1592
1593   // Then proceed.
1594   interstitial->Proceed();
1595   // The interstitial should show until the new navigation commits.
1596   RunAllPendingInMessageLoop();
1597   ASSERT_FALSE(deleted);
1598   EXPECT_EQ(TestInterstitialPage::OKED, state);
1599   EXPECT_TRUE(contents()->ShowingInterstitialPage());
1600   EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1601
1602   // Simulate the navigation to the page, that's when the interstitial gets
1603   // hidden.
1604   GURL url3("http://www.thepage.com");
1605   test_rvh()->SendNavigate(2, url3);
1606
1607   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1608   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1609   entry = controller().GetVisibleEntry();
1610   ASSERT_TRUE(entry != NULL);
1611   EXPECT_TRUE(entry->GetURL() == url3);
1612
1613   EXPECT_EQ(2, controller().GetEntryCount());
1614
1615   RunAllPendingInMessageLoop();
1616   EXPECT_TRUE(deleted);
1617 }
1618
1619 // Test navigating to a page (with the navigation initiated from the renderer,
1620 // as when clicking on a link in the page) that shows an interstitial and
1621 // creates a new navigation entry, then proceeding.
1622 TEST_F(WebContentsImplTest,
1623        ShowInterstitialFromRendererNewNavigationProceed) {
1624   // Navigate to a page.
1625   GURL url1("http://www.google.com");
1626   test_rvh()->SendNavigate(1, url1);
1627   EXPECT_EQ(1, controller().GetEntryCount());
1628
1629   // Show an interstitial.
1630   TestInterstitialPage::InterstitialState state =
1631       TestInterstitialPage::INVALID;
1632   bool deleted = false;
1633   GURL url2("http://interstitial");
1634   TestInterstitialPage* interstitial =
1635       new TestInterstitialPage(contents(), true, url2, &state, &deleted);
1636   TestInterstitialPageStateGuard state_guard(interstitial);
1637   interstitial->Show();
1638   // The interstitial should not show until its navigation has committed.
1639   EXPECT_FALSE(interstitial->is_showing());
1640   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1641   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1642   // Let's commit the interstitial navigation.
1643   interstitial->TestDidNavigate(1, url2);
1644   EXPECT_TRUE(interstitial->is_showing());
1645   EXPECT_TRUE(contents()->ShowingInterstitialPage());
1646   EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1647   NavigationEntry* entry = controller().GetVisibleEntry();
1648   ASSERT_TRUE(entry != NULL);
1649   EXPECT_TRUE(entry->GetURL() == url2);
1650
1651   // Then proceed.
1652   interstitial->Proceed();
1653   // The interstitial should show until the new navigation commits.
1654   RunAllPendingInMessageLoop();
1655   ASSERT_FALSE(deleted);
1656   EXPECT_EQ(TestInterstitialPage::OKED, state);
1657   EXPECT_TRUE(contents()->ShowingInterstitialPage());
1658   EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1659
1660   // Simulate the navigation to the page, that's when the interstitial gets
1661   // hidden.
1662   GURL url3("http://www.thepage.com");
1663   test_rvh()->SendNavigate(2, url3);
1664
1665   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1666   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1667   entry = controller().GetVisibleEntry();
1668   ASSERT_TRUE(entry != NULL);
1669   EXPECT_TRUE(entry->GetURL() == url3);
1670
1671   EXPECT_EQ(2, controller().GetEntryCount());
1672
1673   RunAllPendingInMessageLoop();
1674   EXPECT_TRUE(deleted);
1675 }
1676
1677 // Test navigating to a page that shows an interstitial without creating a new
1678 // navigation entry (this happens when the interstitial is triggered by a
1679 // sub-resource in the page), then proceeding.
1680 TEST_F(WebContentsImplTest, ShowInterstitialNoNewNavigationProceed) {
1681   // Navigate to a page so we have a navigation entry in the controller.
1682   GURL url1("http://www.google.com");
1683   test_rvh()->SendNavigate(1, url1);
1684   EXPECT_EQ(1, controller().GetEntryCount());
1685
1686   // Show an interstitial.
1687   TestInterstitialPage::InterstitialState state =
1688       TestInterstitialPage::INVALID;
1689   bool deleted = false;
1690   GURL url2("http://interstitial");
1691   TestInterstitialPage* interstitial =
1692       new TestInterstitialPage(contents(), false, url2, &state, &deleted);
1693   TestInterstitialPageStateGuard state_guard(interstitial);
1694   interstitial->Show();
1695   // The interstitial should not show until its navigation has committed.
1696   EXPECT_FALSE(interstitial->is_showing());
1697   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1698   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1699   // Let's commit the interstitial navigation.
1700   interstitial->TestDidNavigate(1, url2);
1701   EXPECT_TRUE(interstitial->is_showing());
1702   EXPECT_TRUE(contents()->ShowingInterstitialPage());
1703   EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1704   NavigationEntry* entry = controller().GetVisibleEntry();
1705   ASSERT_TRUE(entry != NULL);
1706   // The URL specified to the interstitial should have been ignored.
1707   EXPECT_TRUE(entry->GetURL() == url1);
1708
1709   // Then proceed.
1710   interstitial->Proceed();
1711   // Since this is not a new navigation, the previous page is dismissed right
1712   // away and shows the original page.
1713   EXPECT_EQ(TestInterstitialPage::OKED, state);
1714   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1715   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1716   entry = controller().GetVisibleEntry();
1717   ASSERT_TRUE(entry != NULL);
1718   EXPECT_TRUE(entry->GetURL() == url1);
1719
1720   EXPECT_EQ(1, controller().GetEntryCount());
1721
1722   RunAllPendingInMessageLoop();
1723   EXPECT_TRUE(deleted);
1724 }
1725
1726 // Test navigating to a page that shows an interstitial, then navigating away.
1727 TEST_F(WebContentsImplTest, ShowInterstitialThenNavigate) {
1728   // Show interstitial.
1729   TestInterstitialPage::InterstitialState state =
1730       TestInterstitialPage::INVALID;
1731   bool deleted = false;
1732   GURL url("http://interstitial");
1733   TestInterstitialPage* interstitial =
1734       new TestInterstitialPage(contents(), true, url, &state, &deleted);
1735   TestInterstitialPageStateGuard state_guard(interstitial);
1736   interstitial->Show();
1737   interstitial->TestDidNavigate(1, url);
1738
1739   // While interstitial showing, navigate to a new URL.
1740   const GURL url2("http://www.yahoo.com");
1741   test_rvh()->SendNavigate(1, url2);
1742
1743   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1744
1745   RunAllPendingInMessageLoop();
1746   EXPECT_TRUE(deleted);
1747 }
1748
1749 // Test navigating to a page that shows an interstitial, then going back.
1750 TEST_F(WebContentsImplTest, ShowInterstitialThenGoBack) {
1751   // Navigate to a page so we have a navigation entry in the controller.
1752   GURL url1("http://www.google.com");
1753   test_rvh()->SendNavigate(1, url1);
1754   EXPECT_EQ(1, controller().GetEntryCount());
1755
1756   // Show interstitial.
1757   TestInterstitialPage::InterstitialState state =
1758       TestInterstitialPage::INVALID;
1759   bool deleted = false;
1760   GURL interstitial_url("http://interstitial");
1761   TestInterstitialPage* interstitial =
1762       new TestInterstitialPage(contents(), true, interstitial_url,
1763                                &state, &deleted);
1764   TestInterstitialPageStateGuard state_guard(interstitial);
1765   interstitial->Show();
1766   interstitial->TestDidNavigate(2, interstitial_url);
1767
1768   // While the interstitial is showing, go back.
1769   controller().GoBack();
1770   test_rvh()->SendNavigate(1, url1);
1771
1772   // Make sure we are back to the original page and that the interstitial is
1773   // gone.
1774   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1775   NavigationEntry* entry = controller().GetVisibleEntry();
1776   ASSERT_TRUE(entry);
1777   EXPECT_EQ(url1.spec(), entry->GetURL().spec());
1778
1779   RunAllPendingInMessageLoop();
1780   EXPECT_TRUE(deleted);
1781 }
1782
1783 // Test navigating to a page that shows an interstitial, has a renderer crash,
1784 // and then goes back.
1785 TEST_F(WebContentsImplTest, ShowInterstitialCrashRendererThenGoBack) {
1786   // Navigate to a page so we have a navigation entry in the controller.
1787   GURL url1("http://www.google.com");
1788   test_rvh()->SendNavigate(1, url1);
1789   EXPECT_EQ(1, controller().GetEntryCount());
1790
1791   // Show interstitial.
1792   TestInterstitialPage::InterstitialState state =
1793       TestInterstitialPage::INVALID;
1794   bool deleted = false;
1795   GURL interstitial_url("http://interstitial");
1796   TestInterstitialPage* interstitial =
1797       new TestInterstitialPage(contents(), true, interstitial_url,
1798                                &state, &deleted);
1799   TestInterstitialPageStateGuard state_guard(interstitial);
1800   interstitial->Show();
1801   interstitial->TestDidNavigate(2, interstitial_url);
1802
1803   // Crash the renderer
1804   test_rvh()->OnMessageReceived(
1805       ViewHostMsg_RenderProcessGone(
1806           0, base::TERMINATION_STATUS_PROCESS_CRASHED, -1));
1807
1808   // While the interstitial is showing, go back.
1809   controller().GoBack();
1810   test_rvh()->SendNavigate(1, url1);
1811
1812   // Make sure we are back to the original page and that the interstitial is
1813   // gone.
1814   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1815   NavigationEntry* entry = controller().GetVisibleEntry();
1816   ASSERT_TRUE(entry);
1817   EXPECT_EQ(url1.spec(), entry->GetURL().spec());
1818
1819   RunAllPendingInMessageLoop();
1820   EXPECT_TRUE(deleted);
1821 }
1822
1823 // Test navigating to a page that shows an interstitial, has the renderer crash,
1824 // and then navigates to the interstitial.
1825 TEST_F(WebContentsImplTest, ShowInterstitialCrashRendererThenNavigate) {
1826   // Navigate to a page so we have a navigation entry in the controller.
1827   GURL url1("http://www.google.com");
1828   test_rvh()->SendNavigate(1, url1);
1829   EXPECT_EQ(1, controller().GetEntryCount());
1830
1831   // Show interstitial.
1832   TestInterstitialPage::InterstitialState state =
1833       TestInterstitialPage::INVALID;
1834   bool deleted = false;
1835   GURL interstitial_url("http://interstitial");
1836   TestInterstitialPage* interstitial =
1837       new TestInterstitialPage(contents(), true, interstitial_url,
1838                                &state, &deleted);
1839   TestInterstitialPageStateGuard state_guard(interstitial);
1840   interstitial->Show();
1841
1842   // Crash the renderer
1843   test_rvh()->OnMessageReceived(
1844       ViewHostMsg_RenderProcessGone(
1845           0, base::TERMINATION_STATUS_PROCESS_CRASHED, -1));
1846
1847   interstitial->TestDidNavigate(2, interstitial_url);
1848 }
1849
1850 // Test navigating to a page that shows an interstitial, then close the
1851 // contents.
1852 TEST_F(WebContentsImplTest, ShowInterstitialThenCloseTab) {
1853   // Show interstitial.
1854   TestInterstitialPage::InterstitialState state =
1855       TestInterstitialPage::INVALID;
1856   bool deleted = false;
1857   GURL url("http://interstitial");
1858   TestInterstitialPage* interstitial =
1859       new TestInterstitialPage(contents(), true, url, &state, &deleted);
1860   TestInterstitialPageStateGuard state_guard(interstitial);
1861   interstitial->Show();
1862   interstitial->TestDidNavigate(1, url);
1863
1864   // Now close the contents.
1865   DeleteContents();
1866   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1867
1868   RunAllPendingInMessageLoop();
1869   EXPECT_TRUE(deleted);
1870 }
1871
1872 // Test navigating to a page that shows an interstitial, then close the
1873 // contents.
1874 TEST_F(WebContentsImplTest, ShowInterstitialThenCloseAndShutdown) {
1875   // Show interstitial.
1876   TestInterstitialPage::InterstitialState state =
1877       TestInterstitialPage::INVALID;
1878   bool deleted = false;
1879   GURL url("http://interstitial");
1880   TestInterstitialPage* interstitial =
1881       new TestInterstitialPage(contents(), true, url, &state, &deleted);
1882   TestInterstitialPageStateGuard state_guard(interstitial);
1883   interstitial->Show();
1884   interstitial->TestDidNavigate(1, url);
1885   RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
1886       interstitial->GetRenderViewHostForTesting());
1887
1888   // Now close the contents.
1889   DeleteContents();
1890   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1891
1892   // Before the interstitial has a chance to process its shutdown task,
1893   // simulate quitting the browser.  This goes through all processes and
1894   // tells them to destruct.
1895   rvh->OnMessageReceived(
1896         ViewHostMsg_RenderProcessGone(0, 0, 0));
1897
1898   RunAllPendingInMessageLoop();
1899   EXPECT_TRUE(deleted);
1900 }
1901
1902 // Test that after Proceed is called and an interstitial is still shown, no more
1903 // commands get executed.
1904 TEST_F(WebContentsImplTest, ShowInterstitialProceedMultipleCommands) {
1905   // Navigate to a page so we have a navigation entry in the controller.
1906   GURL url1("http://www.google.com");
1907   test_rvh()->SendNavigate(1, url1);
1908   EXPECT_EQ(1, controller().GetEntryCount());
1909
1910   // Show an interstitial.
1911   TestInterstitialPage::InterstitialState state =
1912       TestInterstitialPage::INVALID;
1913   bool deleted = false;
1914   GURL url2("http://interstitial");
1915   TestInterstitialPage* interstitial =
1916       new TestInterstitialPage(contents(), true, url2, &state, &deleted);
1917   TestInterstitialPageStateGuard state_guard(interstitial);
1918   interstitial->Show();
1919   interstitial->TestDidNavigate(1, url2);
1920
1921   // Run a command.
1922   EXPECT_EQ(0, interstitial->command_received_count());
1923   interstitial->TestDomOperationResponse("toto");
1924   EXPECT_EQ(1, interstitial->command_received_count());
1925
1926   // Then proceed.
1927   interstitial->Proceed();
1928   RunAllPendingInMessageLoop();
1929   ASSERT_FALSE(deleted);
1930
1931   // While the navigation to the new page is pending, send other commands, they
1932   // should be ignored.
1933   interstitial->TestDomOperationResponse("hello");
1934   interstitial->TestDomOperationResponse("hi");
1935   EXPECT_EQ(1, interstitial->command_received_count());
1936 }
1937
1938 // Test showing an interstitial while another interstitial is already showing.
1939 TEST_F(WebContentsImplTest, ShowInterstitialOnInterstitial) {
1940   // Navigate to a page so we have a navigation entry in the controller.
1941   GURL start_url("http://www.google.com");
1942   test_rvh()->SendNavigate(1, start_url);
1943   EXPECT_EQ(1, controller().GetEntryCount());
1944
1945   // Show an interstitial.
1946   TestInterstitialPage::InterstitialState state1 =
1947       TestInterstitialPage::INVALID;
1948   bool deleted1 = false;
1949   GURL url1("http://interstitial1");
1950   TestInterstitialPage* interstitial1 =
1951       new TestInterstitialPage(contents(), true, url1, &state1, &deleted1);
1952   TestInterstitialPageStateGuard state_guard1(interstitial1);
1953   interstitial1->Show();
1954   interstitial1->TestDidNavigate(1, url1);
1955
1956   // Now show another interstitial.
1957   TestInterstitialPage::InterstitialState state2 =
1958       TestInterstitialPage::INVALID;
1959   bool deleted2 = false;
1960   GURL url2("http://interstitial2");
1961   TestInterstitialPage* interstitial2 =
1962       new TestInterstitialPage(contents(), true, url2, &state2, &deleted2);
1963   TestInterstitialPageStateGuard state_guard2(interstitial2);
1964   interstitial2->Show();
1965   interstitial2->TestDidNavigate(1, url2);
1966
1967   // Showing interstitial2 should have caused interstitial1 to go away.
1968   EXPECT_EQ(TestInterstitialPage::CANCELED, state1);
1969   EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2);
1970
1971   RunAllPendingInMessageLoop();
1972   EXPECT_TRUE(deleted1);
1973   ASSERT_FALSE(deleted2);
1974
1975   // Let's make sure interstitial2 is working as intended.
1976   interstitial2->Proceed();
1977   GURL landing_url("http://www.thepage.com");
1978   test_rvh()->SendNavigate(2, landing_url);
1979
1980   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1981   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1982   NavigationEntry* entry = controller().GetVisibleEntry();
1983   ASSERT_TRUE(entry != NULL);
1984   EXPECT_TRUE(entry->GetURL() == landing_url);
1985   EXPECT_EQ(2, controller().GetEntryCount());
1986   RunAllPendingInMessageLoop();
1987   EXPECT_TRUE(deleted2);
1988 }
1989
1990 // Test showing an interstitial, proceeding and then navigating to another
1991 // interstitial.
1992 TEST_F(WebContentsImplTest, ShowInterstitialProceedShowInterstitial) {
1993   // Navigate to a page so we have a navigation entry in the controller.
1994   GURL start_url("http://www.google.com");
1995   test_rvh()->SendNavigate(1, start_url);
1996   EXPECT_EQ(1, controller().GetEntryCount());
1997
1998   // Show an interstitial.
1999   TestInterstitialPage::InterstitialState state1 =
2000       TestInterstitialPage::INVALID;
2001   bool deleted1 = false;
2002   GURL url1("http://interstitial1");
2003   TestInterstitialPage* interstitial1 =
2004       new TestInterstitialPage(contents(), true, url1, &state1, &deleted1);
2005   TestInterstitialPageStateGuard state_guard1(interstitial1);
2006   interstitial1->Show();
2007   interstitial1->TestDidNavigate(1, url1);
2008
2009   // Take action.  The interstitial won't be hidden until the navigation is
2010   // committed.
2011   interstitial1->Proceed();
2012   EXPECT_EQ(TestInterstitialPage::OKED, state1);
2013
2014   // Now show another interstitial (simulating the navigation causing another
2015   // interstitial).
2016   TestInterstitialPage::InterstitialState state2 =
2017       TestInterstitialPage::INVALID;
2018   bool deleted2 = false;
2019   GURL url2("http://interstitial2");
2020   TestInterstitialPage* interstitial2 =
2021       new TestInterstitialPage(contents(), true, url2, &state2, &deleted2);
2022   TestInterstitialPageStateGuard state_guard2(interstitial2);
2023   interstitial2->Show();
2024   interstitial2->TestDidNavigate(1, url2);
2025
2026   // Showing interstitial2 should have caused interstitial1 to go away.
2027   EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2);
2028   RunAllPendingInMessageLoop();
2029   EXPECT_TRUE(deleted1);
2030   ASSERT_FALSE(deleted2);
2031
2032   // Let's make sure interstitial2 is working as intended.
2033   interstitial2->Proceed();
2034   GURL landing_url("http://www.thepage.com");
2035   test_rvh()->SendNavigate(2, landing_url);
2036
2037   RunAllPendingInMessageLoop();
2038   EXPECT_TRUE(deleted2);
2039   EXPECT_FALSE(contents()->ShowingInterstitialPage());
2040   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
2041   NavigationEntry* entry = controller().GetVisibleEntry();
2042   ASSERT_TRUE(entry != NULL);
2043   EXPECT_TRUE(entry->GetURL() == landing_url);
2044   EXPECT_EQ(2, controller().GetEntryCount());
2045 }
2046
2047 // Test that navigating away from an interstitial while it's loading cause it
2048 // not to show.
2049 TEST_F(WebContentsImplTest, NavigateBeforeInterstitialShows) {
2050   // Show an interstitial.
2051   TestInterstitialPage::InterstitialState state =
2052       TestInterstitialPage::INVALID;
2053   bool deleted = false;
2054   GURL interstitial_url("http://interstitial");
2055   TestInterstitialPage* interstitial =
2056       new TestInterstitialPage(contents(), true, interstitial_url,
2057                                &state, &deleted);
2058   TestInterstitialPageStateGuard state_guard(interstitial);
2059   interstitial->Show();
2060
2061   // Let's simulate a navigation initiated from the browser before the
2062   // interstitial finishes loading.
2063   const GURL url("http://www.google.com");
2064   controller().LoadURL(url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2065   EXPECT_FALSE(interstitial->is_showing());
2066   RunAllPendingInMessageLoop();
2067   ASSERT_FALSE(deleted);
2068
2069   // Now let's make the interstitial navigation commit.
2070   interstitial->TestDidNavigate(1, interstitial_url);
2071
2072   // After it loaded the interstitial should be gone.
2073   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
2074
2075   RunAllPendingInMessageLoop();
2076   EXPECT_TRUE(deleted);
2077 }
2078
2079 // Test that a new request to show an interstitial while an interstitial is
2080 // pending does not cause problems. htp://crbug/29655 and htp://crbug/9442.
2081 TEST_F(WebContentsImplTest, TwoQuickInterstitials) {
2082   GURL interstitial_url("http://interstitial");
2083
2084   // Show a first interstitial.
2085   TestInterstitialPage::InterstitialState state1 =
2086       TestInterstitialPage::INVALID;
2087   bool deleted1 = false;
2088   TestInterstitialPage* interstitial1 =
2089       new TestInterstitialPage(contents(), true, interstitial_url,
2090                                &state1, &deleted1);
2091   TestInterstitialPageStateGuard state_guard1(interstitial1);
2092   interstitial1->Show();
2093
2094   // Show another interstitial on that same contents before the first one had
2095   // time to load.
2096   TestInterstitialPage::InterstitialState state2 =
2097       TestInterstitialPage::INVALID;
2098   bool deleted2 = false;
2099   TestInterstitialPage* interstitial2 =
2100       new TestInterstitialPage(contents(), true, interstitial_url,
2101                                &state2, &deleted2);
2102   TestInterstitialPageStateGuard state_guard2(interstitial2);
2103   interstitial2->Show();
2104
2105   // The first interstitial should have been closed and deleted.
2106   EXPECT_EQ(TestInterstitialPage::CANCELED, state1);
2107   // The 2nd one should still be OK.
2108   EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2);
2109
2110   RunAllPendingInMessageLoop();
2111   EXPECT_TRUE(deleted1);
2112   ASSERT_FALSE(deleted2);
2113
2114   // Make the interstitial navigation commit it should be showing.
2115   interstitial2->TestDidNavigate(1, interstitial_url);
2116   EXPECT_EQ(interstitial2, contents()->GetInterstitialPage());
2117 }
2118
2119 // Test showing an interstitial and have its renderer crash.
2120 TEST_F(WebContentsImplTest, InterstitialCrasher) {
2121   // Show an interstitial.
2122   TestInterstitialPage::InterstitialState state =
2123       TestInterstitialPage::INVALID;
2124   bool deleted = false;
2125   GURL url("http://interstitial");
2126   TestInterstitialPage* interstitial =
2127       new TestInterstitialPage(contents(), true, url, &state, &deleted);
2128   TestInterstitialPageStateGuard state_guard(interstitial);
2129   interstitial->Show();
2130   // Simulate a renderer crash before the interstitial is shown.
2131   interstitial->TestRenderViewTerminated(
2132       base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
2133   // The interstitial should have been dismissed.
2134   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
2135   RunAllPendingInMessageLoop();
2136   EXPECT_TRUE(deleted);
2137
2138   // Now try again but this time crash the intersitial after it was shown.
2139   interstitial =
2140       new TestInterstitialPage(contents(), true, url, &state, &deleted);
2141   interstitial->Show();
2142   interstitial->TestDidNavigate(1, url);
2143   // Simulate a renderer crash.
2144   interstitial->TestRenderViewTerminated(
2145       base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
2146   // The interstitial should have been dismissed.
2147   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
2148   RunAllPendingInMessageLoop();
2149   EXPECT_TRUE(deleted);
2150 }
2151
2152 // Tests that showing an interstitial as a result of a browser initiated
2153 // navigation while an interstitial is showing does not remove the pending
2154 // entry (see http://crbug.com/9791).
2155 TEST_F(WebContentsImplTest, NewInterstitialDoesNotCancelPendingEntry) {
2156   const char kUrl[] = "http://www.badguys.com/";
2157   const GURL kGURL(kUrl);
2158
2159   // Start a navigation to a page
2160   contents()->GetController().LoadURL(
2161       kGURL, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2162
2163   // Simulate that navigation triggering an interstitial.
2164   TestInterstitialPage::InterstitialState state =
2165       TestInterstitialPage::INVALID;
2166   bool deleted = false;
2167   TestInterstitialPage* interstitial =
2168       new TestInterstitialPage(contents(), true, kGURL, &state, &deleted);
2169   TestInterstitialPageStateGuard state_guard(interstitial);
2170   interstitial->Show();
2171   interstitial->TestDidNavigate(1, kGURL);
2172
2173   // Initiate a new navigation from the browser that also triggers an
2174   // interstitial.
2175   contents()->GetController().LoadURL(
2176       kGURL, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2177   TestInterstitialPage::InterstitialState state2 =
2178       TestInterstitialPage::INVALID;
2179   bool deleted2 = false;
2180   TestInterstitialPage* interstitial2 =
2181       new TestInterstitialPage(contents(), true, kGURL, &state2, &deleted2);
2182   TestInterstitialPageStateGuard state_guard2(interstitial2);
2183   interstitial2->Show();
2184   interstitial2->TestDidNavigate(1, kGURL);
2185
2186   // Make sure we still have an entry.
2187   NavigationEntry* entry = contents()->GetController().GetPendingEntry();
2188   ASSERT_TRUE(entry);
2189   EXPECT_EQ(kUrl, entry->GetURL().spec());
2190
2191   // And that the first interstitial is gone, but not the second.
2192   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
2193   EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2);
2194   RunAllPendingInMessageLoop();
2195   EXPECT_TRUE(deleted);
2196   EXPECT_FALSE(deleted2);
2197 }
2198
2199 // Tests that Javascript messages are not shown while an interstitial is
2200 // showing.
2201 TEST_F(WebContentsImplTest, NoJSMessageOnInterstitials) {
2202   const char kUrl[] = "http://www.badguys.com/";
2203   const GURL kGURL(kUrl);
2204
2205   // Start a navigation to a page
2206   contents()->GetController().LoadURL(
2207       kGURL, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2208   // DidNavigate from the page
2209   contents()->TestDidNavigate(rvh(), 1, kGURL, PAGE_TRANSITION_TYPED);
2210
2211   // Simulate showing an interstitial while the page is showing.
2212   TestInterstitialPage::InterstitialState state =
2213       TestInterstitialPage::INVALID;
2214   bool deleted = false;
2215   TestInterstitialPage* interstitial =
2216       new TestInterstitialPage(contents(), true, kGURL, &state, &deleted);
2217   TestInterstitialPageStateGuard state_guard(interstitial);
2218   interstitial->Show();
2219   interstitial->TestDidNavigate(1, kGURL);
2220
2221   // While the interstitial is showing, let's simulate the hidden page
2222   // attempting to show a JS message.
2223   IPC::Message* dummy_message = new IPC::Message;
2224   contents()->RunJavaScriptMessage(contents()->GetMainFrame(),
2225       base::ASCIIToUTF16("This is an informative message"),
2226       base::ASCIIToUTF16("OK"),
2227       kGURL, JAVASCRIPT_MESSAGE_TYPE_ALERT, dummy_message);
2228   EXPECT_TRUE(contents()->last_dialog_suppressed_);
2229 }
2230
2231 // Makes sure that if the source passed to CopyStateFromAndPrune has an
2232 // interstitial it isn't copied over to the destination.
2233 TEST_F(WebContentsImplTest, CopyStateFromAndPruneSourceInterstitial) {
2234   // Navigate to a page.
2235   GURL url1("http://www.google.com");
2236   test_rvh()->SendNavigate(1, url1);
2237   EXPECT_EQ(1, controller().GetEntryCount());
2238
2239   // Initiate a browser navigation that will trigger the interstitial
2240   controller().LoadURL(GURL("http://www.evil.com"), Referrer(),
2241                         PAGE_TRANSITION_TYPED, std::string());
2242
2243   // Show an interstitial.
2244   TestInterstitialPage::InterstitialState state =
2245       TestInterstitialPage::INVALID;
2246   bool deleted = false;
2247   GURL url2("http://interstitial");
2248   TestInterstitialPage* interstitial =
2249       new TestInterstitialPage(contents(), true, url2, &state, &deleted);
2250   TestInterstitialPageStateGuard state_guard(interstitial);
2251   interstitial->Show();
2252   interstitial->TestDidNavigate(1, url2);
2253   EXPECT_TRUE(interstitial->is_showing());
2254   EXPECT_EQ(2, controller().GetEntryCount());
2255
2256   // Create another NavigationController.
2257   GURL url3("http://foo2");
2258   scoped_ptr<TestWebContents> other_contents(
2259       static_cast<TestWebContents*>(CreateTestWebContents()));
2260   NavigationControllerImpl& other_controller = other_contents->GetController();
2261   other_contents->NavigateAndCommit(url3);
2262   other_contents->ExpectSetHistoryLengthAndPrune(
2263       NavigationEntryImpl::FromNavigationEntry(
2264           other_controller.GetEntryAtIndex(0))->site_instance(), 1,
2265       other_controller.GetEntryAtIndex(0)->GetPageID());
2266   other_controller.CopyStateFromAndPrune(&controller(), false);
2267
2268   // The merged controller should only have two entries: url1 and url2.
2269   ASSERT_EQ(2, other_controller.GetEntryCount());
2270   EXPECT_EQ(1, other_controller.GetCurrentEntryIndex());
2271   EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
2272   EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
2273
2274   // And the merged controller shouldn't be showing an interstitial.
2275   EXPECT_FALSE(other_contents->ShowingInterstitialPage());
2276 }
2277
2278 // Makes sure that CopyStateFromAndPrune cannot be called if the target is
2279 // showing an interstitial.
2280 TEST_F(WebContentsImplTest, CopyStateFromAndPruneTargetInterstitial) {
2281   // Navigate to a page.
2282   GURL url1("http://www.google.com");
2283   contents()->NavigateAndCommit(url1);
2284
2285   // Create another NavigationController.
2286   scoped_ptr<TestWebContents> other_contents(
2287       static_cast<TestWebContents*>(CreateTestWebContents()));
2288   NavigationControllerImpl& other_controller = other_contents->GetController();
2289
2290   // Navigate it to url2.
2291   GURL url2("http://foo2");
2292   other_contents->NavigateAndCommit(url2);
2293
2294   // Show an interstitial.
2295   TestInterstitialPage::InterstitialState state =
2296       TestInterstitialPage::INVALID;
2297   bool deleted = false;
2298   GURL url3("http://interstitial");
2299   TestInterstitialPage* interstitial =
2300       new TestInterstitialPage(other_contents.get(), true, url3, &state,
2301                                &deleted);
2302   TestInterstitialPageStateGuard state_guard(interstitial);
2303   interstitial->Show();
2304   interstitial->TestDidNavigate(1, url3);
2305   EXPECT_TRUE(interstitial->is_showing());
2306   EXPECT_EQ(2, other_controller.GetEntryCount());
2307
2308   // Ensure that we do not allow calling CopyStateFromAndPrune when an
2309   // interstitial is showing in the target.
2310   EXPECT_FALSE(other_controller.CanPruneAllButLastCommitted());
2311 }
2312
2313 // Regression test for http://crbug.com/168611 - the URLs passed by the
2314 // DidFinishLoad and DidFailLoadWithError IPCs should get filtered.
2315 TEST_F(WebContentsImplTest, FilterURLs) {
2316   TestWebContentsObserver observer(contents());
2317
2318   // A navigation to about:whatever should always look like a navigation to
2319   // about:blank
2320   GURL url_normalized(url::kAboutBlankURL);
2321   GURL url_from_ipc("about:whatever");
2322
2323   // We navigate the test WebContents to about:blank, since NavigateAndCommit
2324   // will use the given URL to create the NavigationEntry as well, and that
2325   // entry should contain the filtered URL.
2326   contents()->NavigateAndCommit(url_normalized);
2327
2328   // Check that an IPC with about:whatever is correctly normalized.
2329   contents()->TestDidFinishLoad(url_from_ipc);
2330
2331   EXPECT_EQ(url_normalized, observer.last_url());
2332
2333   // Create and navigate another WebContents.
2334   scoped_ptr<TestWebContents> other_contents(
2335       static_cast<TestWebContents*>(CreateTestWebContents()));
2336   TestWebContentsObserver other_observer(other_contents.get());
2337   other_contents->NavigateAndCommit(url_normalized);
2338
2339   // Check that an IPC with about:whatever is correctly normalized.
2340   other_contents->TestDidFailLoadWithError(
2341       url_from_ipc, 1, base::string16());
2342   EXPECT_EQ(url_normalized, other_observer.last_url());
2343 }
2344
2345 // Test that if a pending contents is deleted before it is shown, we don't
2346 // crash.
2347 TEST_F(WebContentsImplTest, PendingContents) {
2348   scoped_ptr<TestWebContents> other_contents(
2349       static_cast<TestWebContents*>(CreateTestWebContents()));
2350   contents()->AddPendingContents(other_contents.get());
2351   int route_id = other_contents->GetRenderViewHost()->GetRoutingID();
2352   other_contents.reset();
2353   EXPECT_EQ(NULL, contents()->GetCreatedWindow(route_id));
2354 }
2355
2356 TEST_F(WebContentsImplTest, CapturerOverridesPreferredSize) {
2357   const gfx::Size original_preferred_size(1024, 768);
2358   contents()->UpdatePreferredSize(original_preferred_size);
2359
2360   // With no capturers, expect the preferred size to be the one propagated into
2361   // WebContentsImpl via the RenderViewHostDelegate interface.
2362   EXPECT_EQ(contents()->GetCapturerCount(), 0);
2363   EXPECT_EQ(original_preferred_size, contents()->GetPreferredSize());
2364
2365   // Increment capturer count, but without specifying a capture size.  Expect
2366   // a "not set" preferred size.
2367   contents()->IncrementCapturerCount(gfx::Size());
2368   EXPECT_EQ(1, contents()->GetCapturerCount());
2369   EXPECT_EQ(gfx::Size(), contents()->GetPreferredSize());
2370
2371   // Increment capturer count again, but with an overriding capture size.
2372   // Expect preferred size to now be overridden to the capture size.
2373   const gfx::Size capture_size(1280, 720);
2374   contents()->IncrementCapturerCount(capture_size);
2375   EXPECT_EQ(2, contents()->GetCapturerCount());
2376   EXPECT_EQ(capture_size, contents()->GetPreferredSize());
2377
2378   // Increment capturer count a third time, but the expect that the preferred
2379   // size is still the first capture size.
2380   const gfx::Size another_capture_size(720, 480);
2381   contents()->IncrementCapturerCount(another_capture_size);
2382   EXPECT_EQ(3, contents()->GetCapturerCount());
2383   EXPECT_EQ(capture_size, contents()->GetPreferredSize());
2384
2385   // Decrement capturer count twice, but expect the preferred size to still be
2386   // overridden.
2387   contents()->DecrementCapturerCount();
2388   contents()->DecrementCapturerCount();
2389   EXPECT_EQ(1, contents()->GetCapturerCount());
2390   EXPECT_EQ(capture_size, contents()->GetPreferredSize());
2391
2392   // Decrement capturer count, and since the count has dropped to zero, the
2393   // original preferred size should be restored.
2394   contents()->DecrementCapturerCount();
2395   EXPECT_EQ(0, contents()->GetCapturerCount());
2396   EXPECT_EQ(original_preferred_size, contents()->GetPreferredSize());
2397 }
2398
2399 // Tests that GetLastActiveTime starts with a real, non-zero time and updates
2400 // on activity.
2401 TEST_F(WebContentsImplTest, GetLastActiveTime) {
2402   // The WebContents starts with a valid creation time.
2403   EXPECT_FALSE(contents()->GetLastActiveTime().is_null());
2404
2405   // Reset the last active time to a known-bad value.
2406   contents()->last_active_time_ = base::TimeTicks();
2407   ASSERT_TRUE(contents()->GetLastActiveTime().is_null());
2408
2409   // Simulate activating the WebContents. The active time should update.
2410   contents()->WasShown();
2411   EXPECT_FALSE(contents()->GetLastActiveTime().is_null());
2412 }
2413
2414 class ContentsZoomChangedDelegate : public WebContentsDelegate {
2415  public:
2416   ContentsZoomChangedDelegate() :
2417     contents_zoom_changed_call_count_(0),
2418     last_zoom_in_(false) {
2419   }
2420
2421   int GetAndResetContentsZoomChangedCallCount() {
2422     int count = contents_zoom_changed_call_count_;
2423     contents_zoom_changed_call_count_ = 0;
2424     return count;
2425   }
2426
2427   bool last_zoom_in() const {
2428     return last_zoom_in_;
2429   }
2430
2431   // WebContentsDelegate:
2432   virtual void ContentsZoomChange(bool zoom_in) OVERRIDE {
2433     contents_zoom_changed_call_count_++;
2434     last_zoom_in_ = zoom_in;
2435   }
2436
2437  private:
2438   int contents_zoom_changed_call_count_;
2439   bool last_zoom_in_;
2440
2441   DISALLOW_COPY_AND_ASSIGN(ContentsZoomChangedDelegate);
2442 };
2443
2444 // Tests that some mouseehweel events get turned into browser zoom requests.
2445 TEST_F(WebContentsImplTest, HandleWheelEvent) {
2446   using blink::WebInputEvent;
2447
2448   scoped_ptr<ContentsZoomChangedDelegate> delegate(
2449       new ContentsZoomChangedDelegate());
2450   contents()->SetDelegate(delegate.get());
2451
2452   int modifiers = 0;
2453   // Verify that normal mouse wheel events do nothing to change the zoom level.
2454   blink::WebMouseWheelEvent event =
2455       SyntheticWebMouseWheelEventBuilder::Build(0, 1, modifiers, false);
2456   EXPECT_FALSE(contents()->HandleWheelEvent(event));
2457   EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
2458
2459   modifiers = WebInputEvent::ShiftKey | WebInputEvent::AltKey;
2460   event = SyntheticWebMouseWheelEventBuilder::Build(0, 1, modifiers, false);
2461   EXPECT_FALSE(contents()->HandleWheelEvent(event));
2462   EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
2463
2464   // But whenever the ctrl modifier is applied, they can increase/decrease zoom.
2465   // Except on MacOS where we never want to adjust zoom with mousewheel.
2466   modifiers = WebInputEvent::ControlKey;
2467   event = SyntheticWebMouseWheelEventBuilder::Build(0, 1, modifiers, false);
2468   bool handled = contents()->HandleWheelEvent(event);
2469 #if defined(OS_MACOSX)
2470   EXPECT_FALSE(handled);
2471   EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
2472 #else
2473   EXPECT_TRUE(handled);
2474   EXPECT_EQ(1, delegate->GetAndResetContentsZoomChangedCallCount());
2475   EXPECT_TRUE(delegate->last_zoom_in());
2476 #endif
2477
2478   modifiers = WebInputEvent::ControlKey | WebInputEvent::ShiftKey |
2479       WebInputEvent::AltKey;
2480   event = SyntheticWebMouseWheelEventBuilder::Build(2, -5, modifiers, false);
2481   handled = contents()->HandleWheelEvent(event);
2482 #if defined(OS_MACOSX)
2483   EXPECT_FALSE(handled);
2484   EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
2485 #else
2486   EXPECT_TRUE(handled);
2487   EXPECT_EQ(1, delegate->GetAndResetContentsZoomChangedCallCount());
2488   EXPECT_FALSE(delegate->last_zoom_in());
2489 #endif
2490
2491   // Unless there is no vertical movement.
2492   event = SyntheticWebMouseWheelEventBuilder::Build(2, 0, modifiers, false);
2493   EXPECT_FALSE(contents()->HandleWheelEvent(event));
2494   EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
2495
2496   // Events containing precise scrolling deltas also shouldn't result in the
2497   // zoom being adjusted, to avoid accidental adjustments caused by
2498   // two-finger-scrolling on a touchpad.
2499   modifiers = WebInputEvent::ControlKey;
2500   event = SyntheticWebMouseWheelEventBuilder::Build(0, 5, modifiers, true);
2501   EXPECT_FALSE(contents()->HandleWheelEvent(event));
2502   EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
2503
2504   // Ensure pointers to the delegate aren't kept beyond its lifetime.
2505   contents()->SetDelegate(NULL);
2506 }
2507
2508 // Tests that trackpad GesturePinchUpdate events get turned into browser zoom.
2509 TEST_F(WebContentsImplTest, HandleGestureEvent) {
2510   using blink::WebGestureEvent;
2511   using blink::WebInputEvent;
2512
2513   scoped_ptr<ContentsZoomChangedDelegate> delegate(
2514       new ContentsZoomChangedDelegate());
2515   contents()->SetDelegate(delegate.get());
2516
2517   const float kZoomStepValue = 0.6f;
2518   blink::WebGestureEvent event = SyntheticWebGestureEventBuilder::Build(
2519       WebInputEvent::GesturePinchUpdate, blink::WebGestureDeviceTouchpad);
2520
2521   // A pinch less than the step value doesn't change the zoom level.
2522   event.data.pinchUpdate.scale = 1.0f + kZoomStepValue * 0.8f;
2523   EXPECT_TRUE(contents()->HandleGestureEvent(event));
2524   EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
2525
2526   // But repeating the event so the combined scale is greater does.
2527   EXPECT_TRUE(contents()->HandleGestureEvent(event));
2528   EXPECT_EQ(1, delegate->GetAndResetContentsZoomChangedCallCount());
2529   EXPECT_TRUE(delegate->last_zoom_in());
2530
2531   // Pinching back out one step goes back to 100%.
2532   event.data.pinchUpdate.scale = 1.0f - kZoomStepValue;
2533   EXPECT_TRUE(contents()->HandleGestureEvent(event));
2534   EXPECT_EQ(1, delegate->GetAndResetContentsZoomChangedCallCount());
2535   EXPECT_FALSE(delegate->last_zoom_in());
2536
2537   // Pinching out again doesn't zoom (step is twice as large around 100%).
2538   EXPECT_TRUE(contents()->HandleGestureEvent(event));
2539   EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
2540
2541   // And again now it zooms once per step.
2542   EXPECT_TRUE(contents()->HandleGestureEvent(event));
2543   EXPECT_EQ(1, delegate->GetAndResetContentsZoomChangedCallCount());
2544   EXPECT_FALSE(delegate->last_zoom_in());
2545
2546   // No other type of gesture event is handled by WebContentsImpl (for example
2547   // a touchscreen pinch gesture).
2548   event = SyntheticWebGestureEventBuilder::Build(
2549       WebInputEvent::GesturePinchUpdate, blink::WebGestureDeviceTouchscreen);
2550   event.data.pinchUpdate.scale = 1.0f + kZoomStepValue * 3;
2551   EXPECT_FALSE(contents()->HandleGestureEvent(event));
2552   EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
2553
2554   // Ensure pointers to the delegate aren't kept beyond it's lifetime.
2555   contents()->SetDelegate(NULL);
2556 }
2557
2558 // Tests that GetRelatedActiveContentsCount is shared between related
2559 // SiteInstances and includes WebContents that have not navigated yet.
2560 TEST_F(WebContentsImplTest, ActiveContentsCountBasic) {
2561   scoped_refptr<SiteInstance> instance1(
2562       SiteInstance::CreateForURL(browser_context(), GURL("http://a.com")));
2563   scoped_refptr<SiteInstance> instance2(
2564       instance1->GetRelatedSiteInstance(GURL("http://b.com")));
2565
2566   EXPECT_EQ(0u, instance1->GetRelatedActiveContentsCount());
2567   EXPECT_EQ(0u, instance2->GetRelatedActiveContentsCount());
2568
2569   scoped_ptr<TestWebContents> contents1(
2570       TestWebContents::Create(browser_context(), instance1));
2571   EXPECT_EQ(1u, instance1->GetRelatedActiveContentsCount());
2572   EXPECT_EQ(1u, instance2->GetRelatedActiveContentsCount());
2573
2574   scoped_ptr<TestWebContents> contents2(
2575       TestWebContents::Create(browser_context(), instance1));
2576   EXPECT_EQ(2u, instance1->GetRelatedActiveContentsCount());
2577   EXPECT_EQ(2u, instance2->GetRelatedActiveContentsCount());
2578
2579   contents1.reset();
2580   EXPECT_EQ(1u, instance1->GetRelatedActiveContentsCount());
2581   EXPECT_EQ(1u, instance2->GetRelatedActiveContentsCount());
2582
2583   contents2.reset();
2584   EXPECT_EQ(0u, instance1->GetRelatedActiveContentsCount());
2585   EXPECT_EQ(0u, instance2->GetRelatedActiveContentsCount());
2586 }
2587
2588 // Tests that GetRelatedActiveContentsCount is preserved correctly across
2589 // same-site and cross-site navigations.
2590 TEST_F(WebContentsImplTest, ActiveContentsCountNavigate) {
2591   scoped_refptr<SiteInstance> instance(
2592       SiteInstance::Create(browser_context()));
2593
2594   EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
2595
2596   scoped_ptr<TestWebContents> contents(
2597       TestWebContents::Create(browser_context(), instance));
2598   EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2599
2600   // Navigate to a URL.
2601   contents->GetController().LoadURL(
2602       GURL("http://a.com/1"), Referrer(), PAGE_TRANSITION_TYPED, std::string());
2603   EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2604   contents->CommitPendingNavigation();
2605   EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2606
2607   // Navigate to a URL in the same site.
2608   contents->GetController().LoadURL(
2609       GURL("http://a.com/2"), Referrer(), PAGE_TRANSITION_TYPED, std::string());
2610   EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2611   contents->CommitPendingNavigation();
2612   EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2613
2614   // Navigate to a URL in a different site.
2615   contents->GetController().LoadURL(
2616       GURL("http://b.com"), Referrer(), PAGE_TRANSITION_TYPED, std::string());
2617   EXPECT_TRUE(contents->cross_navigation_pending());
2618   EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2619   contents->CommitPendingNavigation();
2620   EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2621
2622   contents.reset();
2623   EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
2624 }
2625
2626 // Tests that GetRelatedActiveContentsCount tracks BrowsingInstance changes
2627 // from WebUI.
2628 TEST_F(WebContentsImplTest, ActiveContentsCountChangeBrowsingInstance) {
2629   scoped_refptr<SiteInstance> instance(
2630       SiteInstance::Create(browser_context()));
2631
2632   EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
2633
2634   scoped_ptr<TestWebContents> contents(
2635       TestWebContents::Create(browser_context(), instance));
2636   EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2637
2638   // Navigate to a URL.
2639   contents->NavigateAndCommit(GURL("http://a.com"));
2640   EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2641
2642   // Navigate to a URL with WebUI. This will change BrowsingInstances.
2643   contents->GetController().LoadURL(
2644       GURL(kTestWebUIUrl), Referrer(), PAGE_TRANSITION_TYPED, std::string());
2645   EXPECT_TRUE(contents->cross_navigation_pending());
2646   scoped_refptr<SiteInstance> instance_webui(
2647       contents->GetPendingRenderViewHost()->GetSiteInstance());
2648   EXPECT_FALSE(instance->IsRelatedSiteInstance(instance_webui.get()));
2649
2650   // At this point, contents still counts for the old BrowsingInstance.
2651   EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2652   EXPECT_EQ(0u, instance_webui->GetRelatedActiveContentsCount());
2653
2654   // Commit and contents counts for the new one.
2655   contents->CommitPendingNavigation();
2656   EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
2657   EXPECT_EQ(1u, instance_webui->GetRelatedActiveContentsCount());
2658
2659   contents.reset();
2660   EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
2661   EXPECT_EQ(0u, instance_webui->GetRelatedActiveContentsCount());
2662 }
2663
2664 }  // namespace content