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