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
39namespace content {
40namespace {
41
42const char kTestWebUIUrl[] = "chrome://blah";
43
44class 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
75class TestInterstitialPage;
76
77class 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
89class 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
200void TestInterstitialPageDelegate::CommandReceived(const std::string& command) {
201  interstitial_page_->CommandReceived();
202}
203
204void TestInterstitialPageDelegate::OnDontProceed() {
205  interstitial_page_->OnDontProceed();
206}
207
208void TestInterstitialPageDelegate::OnProceed() {
209  interstitial_page_->OnProceed();
210}
211
212class 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
235class 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
254class 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
270class 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.
298class 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
319class 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.
342TEST_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
358TEST_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
365TEST_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.
379TEST_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.
403TEST_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.
426TEST_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.
457TEST_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.
469TEST_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).
562TEST_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.
609TEST_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.
668TEST_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.
759TEST_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.
803TEST_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.
846TEST_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.
875TEST_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.
924TEST_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.
976TEST_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
1010TEST_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).
1114TEST_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.
1151TEST_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
1184TEST_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.
1229TEST_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.
1266TEST_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.
1300TEST_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().
1341TEST_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
1394TEST_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.
1412TEST_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.
1453TEST_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.
1503TEST_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.
1550TEST_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.
1596TEST_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.
1658TEST_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.
1716TEST_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.
1763TEST_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.
1786TEST_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.
1821TEST_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.
1861TEST_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.
1888TEST_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.
1910TEST_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.
1940TEST_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.
1975TEST_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.
2028TEST_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.
2085TEST_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.
2118TEST_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.
2157TEST_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).
2192TEST_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.
2238TEST_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.
2271TEST_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.
2318TEST_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.
2353TEST_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.
2385TEST_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
2394TEST_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.
2439TEST_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
2452class 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.
2483TEST_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.
2547TEST_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.
2598TEST_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.
2628TEST_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.
2672TEST_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)
2712TEST_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  const int kPlayerRemoteId = -30;
2719
2720  EXPECT_FALSE(contents()->has_audio_power_save_blocker_for_testing());
2721  EXPECT_FALSE(contents()->has_video_power_save_blocker_for_testing());
2722
2723  TestRenderFrameHost* rfh = contents()->GetMainFrame();
2724  AudioStreamMonitor* monitor = contents()->audio_stream_monitor();
2725
2726  // The audio power save blocker should not be based on having a media player
2727  // when audio stream monitoring is available.
2728  if (AudioStreamMonitor::monitoring_available()) {
2729    // Send a fake audio stream monitor notification.  The audio power save
2730    // blocker should be created.
2731    monitor->set_was_recently_audible_for_testing(true);
2732    contents()->NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB);
2733    EXPECT_TRUE(contents()->has_audio_power_save_blocker_for_testing());
2734
2735    // Send another fake notification, this time when WasRecentlyAudible() will
2736    // be false.  The power save blocker should be released.
2737    monitor->set_was_recently_audible_for_testing(false);
2738    contents()->NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB);
2739    EXPECT_FALSE(contents()->has_audio_power_save_blocker_for_testing());
2740  }
2741
2742  // Start a player with both audio and video.  A video power save blocker
2743  // should be created.  If audio stream monitoring is available, an audio power
2744  // save blocker should be created too.
2745  rfh->OnMessageReceived(FrameHostMsg_MediaPlayingNotification(
2746      0, kPlayerAudioVideoId, true, true, false));
2747  EXPECT_TRUE(contents()->has_video_power_save_blocker_for_testing());
2748  EXPECT_EQ(contents()->has_audio_power_save_blocker_for_testing(),
2749            !AudioStreamMonitor::monitoring_available());
2750
2751  // Upon hiding the video power save blocker should be released.
2752  contents()->WasHidden();
2753  EXPECT_FALSE(contents()->has_video_power_save_blocker_for_testing());
2754
2755  // Start another player that only has video.  There should be no change in
2756  // the power save blockers.  The notification should take into account the
2757  // visibility state of the WebContents.
2758  rfh->OnMessageReceived(FrameHostMsg_MediaPlayingNotification(
2759      0, kPlayerVideoOnlyId, true, false, false));
2760  EXPECT_FALSE(contents()->has_video_power_save_blocker_for_testing());
2761  EXPECT_EQ(contents()->has_audio_power_save_blocker_for_testing(),
2762            !AudioStreamMonitor::monitoring_available());
2763
2764  // Showing the WebContents should result in the creation of the blocker.
2765  contents()->WasShown();
2766  EXPECT_TRUE(contents()->has_video_power_save_blocker_for_testing());
2767
2768  // Start another player that only has audio.  There should be no change in
2769  // the power save blockers.
2770  rfh->OnMessageReceived(FrameHostMsg_MediaPlayingNotification(
2771      0, kPlayerAudioOnlyId, false, true, false));
2772  EXPECT_TRUE(contents()->has_video_power_save_blocker_for_testing());
2773  EXPECT_EQ(contents()->has_audio_power_save_blocker_for_testing(),
2774            !AudioStreamMonitor::monitoring_available());
2775
2776  // Start a remote player. There should be no change in the power save
2777  // blockers.
2778  rfh->OnMessageReceived(FrameHostMsg_MediaPlayingNotification(
2779      0, kPlayerRemoteId, true, true, true));
2780  EXPECT_TRUE(contents()->has_video_power_save_blocker_for_testing());
2781  EXPECT_EQ(contents()->has_audio_power_save_blocker_for_testing(),
2782            !AudioStreamMonitor::monitoring_available());
2783
2784  // Destroy the original audio video player.  Both power save blockers should
2785  // remain.
2786  rfh->OnMessageReceived(
2787      FrameHostMsg_MediaPausedNotification(0, kPlayerAudioVideoId));
2788  EXPECT_TRUE(contents()->has_video_power_save_blocker_for_testing());
2789  EXPECT_EQ(contents()->has_audio_power_save_blocker_for_testing(),
2790            !AudioStreamMonitor::monitoring_available());
2791
2792  // Destroy the audio only player.  The video power save blocker should remain.
2793  rfh->OnMessageReceived(
2794      FrameHostMsg_MediaPausedNotification(0, kPlayerAudioOnlyId));
2795  EXPECT_TRUE(contents()->has_video_power_save_blocker_for_testing());
2796  EXPECT_FALSE(contents()->has_audio_power_save_blocker_for_testing());
2797
2798  // Destroy the video only player.  No power save blockers should remain.
2799  rfh->OnMessageReceived(
2800      FrameHostMsg_MediaPausedNotification(0, kPlayerVideoOnlyId));
2801  EXPECT_FALSE(contents()->has_video_power_save_blocker_for_testing());
2802  EXPECT_FALSE(contents()->has_audio_power_save_blocker_for_testing());
2803
2804  // Destroy the remote player. No power save blockers should remain.
2805  rfh->OnMessageReceived(
2806      FrameHostMsg_MediaPausedNotification(0, kPlayerRemoteId));
2807  EXPECT_FALSE(contents()->has_video_power_save_blocker_for_testing());
2808  EXPECT_FALSE(contents()->has_audio_power_save_blocker_for_testing());
2809}
2810#endif
2811
2812}  // namespace content
2813