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