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