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