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