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