Home | History | Annotate | Download | only in captive_portal
      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 "chrome/browser/captive_portal/captive_portal_tab_helper.h"
      6 
      7 #include "base/callback.h"
      8 #include "base/memory/scoped_ptr.h"
      9 #include "chrome/browser/captive_portal/captive_portal_service.h"
     10 #include "chrome/browser/captive_portal/captive_portal_tab_reloader.h"
     11 #include "chrome/browser/chrome_notification_types.h"
     12 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
     13 #include "content/public/browser/notification_details.h"
     14 #include "content/public/browser/notification_service.h"
     15 #include "content/public/browser/notification_source.h"
     16 #include "content/public/browser/notification_types.h"
     17 #include "content/public/browser/render_view_host.h"
     18 #include "content/public/browser/render_process_host.h"
     19 #include "content/public/browser/web_contents.h"
     20 #include "net/base/net_errors.h"
     21 #include "testing/gmock/include/gmock/gmock.h"
     22 #include "testing/gtest/include/gtest/gtest.h"
     23 
     24 using captive_portal::CaptivePortalResult;
     25 
     26 namespace {
     27 
     28 const char* const kHttpUrl = "http://whatever.com/";
     29 const char* const kHttpsUrl = "https://whatever.com/";
     30 
     31 // Used for cross-process navigations.  Shouldn't actually matter whether this
     32 // is different from kHttpsUrl, but best to keep things consistent.
     33 const char* const kHttpsUrl2 = "https://cross_process.com/";
     34 
     35 // Error pages use a "data:" URL.  Shouldn't actually matter what this is.
     36 const char* const kErrorPageUrl = "data:blah";
     37 
     38 // Some navigations behave differently depending on if they're cross-process
     39 // or not.
     40 enum NavigationType {
     41   kSameProcess,
     42   kCrossProcess,
     43 };
     44 
     45 }  // namespace
     46 
     47 class MockCaptivePortalTabReloader : public CaptivePortalTabReloader {
     48  public:
     49   MockCaptivePortalTabReloader()
     50       : CaptivePortalTabReloader(NULL, NULL, base::Callback<void()>()) {
     51   }
     52 
     53   MOCK_METHOD1(OnLoadStart, void(bool));
     54   MOCK_METHOD1(OnLoadCommitted, void(int));
     55   MOCK_METHOD0(OnAbort, void());
     56   MOCK_METHOD1(OnRedirect, void(bool));
     57   MOCK_METHOD2(OnCaptivePortalResults,
     58                void(CaptivePortalResult, CaptivePortalResult));
     59 };
     60 
     61 // Inherits from the ChromeRenderViewHostTestHarness to gain access to
     62 // CreateTestWebContents.  Since the tests need to micromanage order of
     63 // WebContentsObserver function calls, does not actually make sure of
     64 // the harness in any other way.
     65 class CaptivePortalTabHelperTest : public ChromeRenderViewHostTestHarness {
     66  public:
     67   CaptivePortalTabHelperTest()
     68       : tab_helper_(NULL),
     69         mock_reloader_(new testing::StrictMock<MockCaptivePortalTabReloader>) {
     70     tab_helper_.SetTabReloaderForTest(mock_reloader_);
     71   }
     72   virtual ~CaptivePortalTabHelperTest() {}
     73 
     74   virtual void SetUp() OVERRIDE {
     75     ChromeRenderViewHostTestHarness::SetUp();
     76     web_contents1_.reset(CreateTestWebContents());
     77     web_contents2_.reset(CreateTestWebContents());
     78   }
     79 
     80   virtual void TearDown() OVERRIDE {
     81     web_contents2_.reset(NULL);
     82     web_contents1_.reset(NULL);
     83     ChromeRenderViewHostTestHarness::TearDown();
     84   }
     85 
     86   // Simulates a successful load of |url|.
     87   void SimulateSuccess(const GURL& url,
     88                        content::RenderViewHost* render_view_host) {
     89     EXPECT_CALL(mock_reloader(), OnLoadStart(url.SchemeIsSecure())).Times(1);
     90     tab_helper().DidStartProvisionalLoadForFrame(
     91         1, -1, true, url, false, false, render_view_host);
     92 
     93     EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::OK)).Times(1);
     94     tab_helper().DidCommitProvisionalLoadForFrame(
     95         1, base::string16(), true, url, content::PAGE_TRANSITION_LINK,
     96         render_view_host);
     97   }
     98 
     99   // Simulates a connection timeout while requesting |url|.
    100   void SimulateTimeout(const GURL& url,
    101                        content::RenderViewHost* render_view_host) {
    102     EXPECT_CALL(mock_reloader(), OnLoadStart(url.SchemeIsSecure())).Times(1);
    103     tab_helper().DidStartProvisionalLoadForFrame(
    104         1, -1, true, url, false, false, render_view_host);
    105 
    106     tab_helper().DidFailProvisionalLoad(
    107         1, base::string16(), true, url, net::ERR_TIMED_OUT, base::string16(),
    108         render_view_host);
    109 
    110     // Provisional load starts for the error page.
    111     tab_helper().DidStartProvisionalLoadForFrame(
    112         1, -1, true, GURL(kErrorPageUrl), true, false, render_view_host);
    113 
    114     EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::ERR_TIMED_OUT)).Times(1);
    115     tab_helper().DidCommitProvisionalLoadForFrame(
    116         1, base::string16(), true, GURL(kErrorPageUrl),
    117         content::PAGE_TRANSITION_LINK, render_view_host);
    118   }
    119 
    120   // Simulates an abort while requesting |url|.
    121   void SimulateAbort(const GURL& url,
    122                      content::RenderViewHost* render_view_host,
    123                      NavigationType navigation_type) {
    124     EXPECT_CALL(mock_reloader(), OnLoadStart(url.SchemeIsSecure())).Times(1);
    125     tab_helper().DidStartProvisionalLoadForFrame(
    126         1, -1, true, url, false, false, render_view_host);
    127 
    128     EXPECT_CALL(mock_reloader(), OnAbort()).Times(1);
    129     if (navigation_type == kSameProcess) {
    130       tab_helper().DidFailProvisionalLoad(
    131           1, base::string16(), true, url, net::ERR_ABORTED, base::string16(),
    132           render_view_host);
    133     } else {
    134       // For interrupted provisional cross-process navigations, the
    135       // RenderViewHost is destroyed without sending a DidFailProvisionalLoad
    136       // notification.
    137       tab_helper().RenderViewDeleted(render_view_host);
    138     }
    139 
    140     // Make sure that above call resulted in abort, for tests that continue
    141     // after the abort.
    142     EXPECT_CALL(mock_reloader(), OnAbort()).Times(0);
    143   }
    144 
    145   // Simulates an abort while loading an error page.
    146   void SimulateAbortTimeout(const GURL& url,
    147                             content::RenderViewHost* render_view_host,
    148                             NavigationType navigation_type) {
    149     EXPECT_CALL(mock_reloader(), OnLoadStart(url.SchemeIsSecure())).Times(1);
    150     tab_helper().DidStartProvisionalLoadForFrame(
    151         1, -1, true, url, false, false, render_view_host);
    152 
    153     tab_helper().DidFailProvisionalLoad(
    154         1, base::string16(), true, url, net::ERR_TIMED_OUT, base::string16(),
    155         render_view_host);
    156 
    157     // Start event for the error page.
    158     tab_helper().DidStartProvisionalLoadForFrame(
    159         1, -1, true, url, true, false, render_view_host);
    160 
    161     EXPECT_CALL(mock_reloader(), OnAbort()).Times(1);
    162     if (navigation_type == kSameProcess) {
    163       tab_helper().DidFailProvisionalLoad(
    164           1, base::string16(), true, url, net::ERR_ABORTED, base::string16(),
    165           render_view_host);
    166     } else {
    167       // For interrupted provisional cross-process navigations, the
    168       // RenderViewHost is destroyed without sending a DidFailProvisionalLoad
    169       // notification.
    170       tab_helper().RenderViewDeleted(render_view_host);
    171     }
    172 
    173     // Make sure that above call resulted in abort, for tests that continue
    174     // after the abort.
    175     EXPECT_CALL(mock_reloader(), OnAbort()).Times(0);
    176   }
    177 
    178   CaptivePortalTabHelper& tab_helper() {
    179     return tab_helper_;
    180   }
    181 
    182   // Simulates a captive portal redirect by calling the Observe method.
    183   void ObservePortalResult(CaptivePortalResult previous_result,
    184                            CaptivePortalResult result) {
    185     content::Source<Profile> source_profile(NULL);
    186 
    187     CaptivePortalService::Results results;
    188     results.previous_result = previous_result;
    189     results.result = result;
    190     content::Details<CaptivePortalService::Results> details_results(&results);
    191 
    192     EXPECT_CALL(mock_reloader(), OnCaptivePortalResults(previous_result,
    193                                                         result)).Times(1);
    194     tab_helper().Observe(chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT,
    195                          source_profile,
    196                          details_results);
    197   }
    198 
    199   // Simulates a redirect.  Uses OnRedirect rather than Observe, for simplicity.
    200   void OnRedirect(ResourceType::Type type, const GURL& new_url, int child_id) {
    201     tab_helper().OnRedirect(child_id, type, new_url);
    202   }
    203 
    204   MockCaptivePortalTabReloader& mock_reloader() { return *mock_reloader_; }
    205 
    206   void SetIsLoginTab() {
    207     tab_helper().SetIsLoginTab();
    208   }
    209 
    210   content::RenderViewHost* render_view_host1() {
    211     return web_contents1_->GetRenderViewHost();
    212   }
    213 
    214   content::RenderViewHost* render_view_host2() {
    215     return web_contents2_->GetRenderViewHost();
    216   }
    217 
    218  private:
    219   // Only the RenderViewHosts are used.
    220   scoped_ptr<content::WebContents> web_contents1_;
    221   scoped_ptr<content::WebContents> web_contents2_;
    222 
    223   CaptivePortalTabHelper tab_helper_;
    224 
    225   // Owned by |tab_helper_|.
    226   testing::StrictMock<MockCaptivePortalTabReloader>* mock_reloader_;
    227 
    228   DISALLOW_COPY_AND_ASSIGN(CaptivePortalTabHelperTest);
    229 };
    230 
    231 TEST_F(CaptivePortalTabHelperTest, HttpSuccess) {
    232   SimulateSuccess(GURL(kHttpUrl), render_view_host1());
    233   tab_helper().DidStopLoading(render_view_host1());
    234 }
    235 
    236 TEST_F(CaptivePortalTabHelperTest, HttpTimeout) {
    237   SimulateTimeout(GURL(kHttpUrl), render_view_host1());
    238   tab_helper().DidStopLoading(render_view_host1());
    239 }
    240 
    241 // Same as above, but simulates what happens when the Link Doctor is enabled,
    242 // which adds another provisional load/commit for the error page, after the
    243 // first two.
    244 TEST_F(CaptivePortalTabHelperTest, HttpTimeoutLinkDoctor) {
    245   SimulateTimeout(GURL(kHttpUrl), render_view_host1());
    246 
    247   EXPECT_CALL(mock_reloader(), OnLoadStart(false)).Times(1);
    248   // Provisional load starts for the error page.
    249   tab_helper().DidStartProvisionalLoadForFrame(
    250       1, -1, true, GURL(kErrorPageUrl), true, false, render_view_host1());
    251 
    252   EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::OK)).Times(1);
    253   tab_helper().DidCommitProvisionalLoadForFrame(
    254       1, base::string16(), true, GURL(kErrorPageUrl),
    255       content::PAGE_TRANSITION_LINK, render_view_host1());
    256   tab_helper().DidStopLoading(render_view_host1());
    257 }
    258 
    259 TEST_F(CaptivePortalTabHelperTest, HttpsSuccess) {
    260   SimulateSuccess(GURL(kHttpsUrl), render_view_host1());
    261   tab_helper().DidStopLoading(render_view_host1());
    262   EXPECT_FALSE(tab_helper().IsLoginTab());
    263 }
    264 
    265 TEST_F(CaptivePortalTabHelperTest, HttpsTimeout) {
    266   SimulateTimeout(GURL(kHttpsUrl), render_view_host1());
    267   // Make sure no state was carried over from the timeout.
    268   SimulateSuccess(GURL(kHttpsUrl), render_view_host1());
    269   EXPECT_FALSE(tab_helper().IsLoginTab());
    270 }
    271 
    272 TEST_F(CaptivePortalTabHelperTest, HttpsAbort) {
    273   SimulateAbort(GURL(kHttpsUrl), render_view_host1(), kSameProcess);
    274   // Make sure no state was carried over from the abort.
    275   SimulateSuccess(GURL(kHttpsUrl), render_view_host1());
    276   EXPECT_FALSE(tab_helper().IsLoginTab());
    277 }
    278 
    279 // A cross-process navigation is aborted by a same-site navigation.
    280 TEST_F(CaptivePortalTabHelperTest, AbortCrossProcess) {
    281   SimulateAbort(GURL(kHttpsUrl2), render_view_host2(), kCrossProcess);
    282   // Make sure no state was carried over from the abort.
    283   SimulateSuccess(GURL(kHttpUrl), render_view_host1());
    284   EXPECT_FALSE(tab_helper().IsLoginTab());
    285 }
    286 
    287 // Abort while there's a provisional timeout error page loading.
    288 TEST_F(CaptivePortalTabHelperTest, HttpsAbortTimeout) {
    289   SimulateAbortTimeout(GURL(kHttpsUrl), render_view_host1(), kSameProcess);
    290   // Make sure no state was carried over from the timeout or the abort.
    291   SimulateSuccess(GURL(kHttpsUrl), render_view_host1());
    292   EXPECT_FALSE(tab_helper().IsLoginTab());
    293 }
    294 
    295 // Abort a cross-process navigation while there's a provisional timeout error
    296 // page loading.
    297 TEST_F(CaptivePortalTabHelperTest, AbortTimeoutCrossProcess) {
    298   SimulateAbortTimeout(GURL(kHttpsUrl2), render_view_host2(),
    299                        kCrossProcess);
    300   // Make sure no state was carried over from the timeout or the abort.
    301   SimulateSuccess(GURL(kHttpsUrl), render_view_host1());
    302   EXPECT_FALSE(tab_helper().IsLoginTab());
    303 }
    304 
    305 // Opposite case from above - a same-process error page is aborted in favor of
    306 // a cross-process one.
    307 TEST_F(CaptivePortalTabHelperTest, HttpsAbortTimeoutForCrossProcess) {
    308   SimulateAbortTimeout(GURL(kHttpsUrl), render_view_host1(), kSameProcess);
    309   // Make sure no state was carried over from the timeout or the abort.
    310   SimulateSuccess(GURL(kHttpsUrl2), render_view_host2());
    311   EXPECT_FALSE(tab_helper().IsLoginTab());
    312 }
    313 
    314 // A provisional same-site navigation is interrupted by a cross-process
    315 // navigation without sending an abort first.
    316 TEST_F(CaptivePortalTabHelperTest, UnexpectedProvisionalLoad) {
    317   GURL same_site_url = GURL(kHttpUrl);
    318   GURL cross_process_url = GURL(kHttpsUrl2);
    319 
    320   // A same-site load for the original RenderViewHost starts.
    321   EXPECT_CALL(mock_reloader(),
    322               OnLoadStart(same_site_url.SchemeIsSecure())).Times(1);
    323   tab_helper().DidStartProvisionalLoadForFrame(
    324       1, -1, true, same_site_url, false, false, render_view_host1());
    325 
    326   // It's unexpectedly interrupted by a cross-process navigation, which starts
    327   // navigating before the old navigation cancels.  We generate an abort message
    328   // for the old navigation.
    329   EXPECT_CALL(mock_reloader(), OnAbort()).Times(1);
    330   EXPECT_CALL(mock_reloader(),
    331               OnLoadStart(cross_process_url.SchemeIsSecure())).Times(1);
    332   tab_helper().DidStartProvisionalLoadForFrame(
    333       1, -1, true, cross_process_url, false, false, render_view_host2());
    334 
    335   // The cross-process navigation fails.
    336   tab_helper().DidFailProvisionalLoad(
    337       1, base::string16(), true, cross_process_url, net::ERR_FAILED,
    338       base::string16(), render_view_host2());
    339 
    340   // The same-site navigation finally is aborted.
    341   tab_helper().DidFailProvisionalLoad(
    342       1, base::string16(), true, same_site_url, net::ERR_ABORTED,
    343       base::string16(), render_view_host1());
    344 
    345   // The provisional load starts for the error page for the cross-process
    346   // navigation.
    347   tab_helper().DidStartProvisionalLoadForFrame(
    348       1, -1, true, GURL(kErrorPageUrl), true, false, render_view_host2());
    349 
    350   EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::ERR_FAILED)).Times(1);
    351   tab_helper().DidCommitProvisionalLoadForFrame(
    352       1, base::string16(), true, GURL(kErrorPageUrl),
    353       content::PAGE_TRANSITION_TYPED, render_view_host2());
    354 }
    355 
    356 // Similar to the above test, except the original RenderViewHost manages to
    357 // commit before its navigation is aborted.
    358 TEST_F(CaptivePortalTabHelperTest, UnexpectedCommit) {
    359   GURL same_site_url = GURL(kHttpUrl);
    360   GURL cross_process_url = GURL(kHttpsUrl2);
    361 
    362   // A same-site load for the original RenderViewHost starts.
    363   EXPECT_CALL(mock_reloader(),
    364               OnLoadStart(same_site_url.SchemeIsSecure())).Times(1);
    365   tab_helper().DidStartProvisionalLoadForFrame(
    366       1, -1, true, same_site_url, false, false, render_view_host1());
    367 
    368   // It's unexpectedly interrupted by a cross-process navigation, which starts
    369   // navigating before the old navigation cancels.  We generate an abort message
    370   // for the old navigation.
    371   EXPECT_CALL(mock_reloader(), OnAbort()).Times(1);
    372   EXPECT_CALL(mock_reloader(),
    373               OnLoadStart(cross_process_url.SchemeIsSecure())).Times(1);
    374   tab_helper().DidStartProvisionalLoadForFrame(
    375       1, -1, true, cross_process_url, false, false, render_view_host2());
    376 
    377   // The cross-process navigation fails.
    378   tab_helper().DidFailProvisionalLoad(
    379       1, base::string16(), true, cross_process_url, net::ERR_FAILED,
    380       base::string16(),
    381       render_view_host2());
    382 
    383   // The same-site navigation succeeds.
    384   EXPECT_CALL(mock_reloader(), OnAbort()).Times(1);
    385   EXPECT_CALL(mock_reloader(),
    386               OnLoadStart(same_site_url.SchemeIsSecure())).Times(1);
    387   EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::OK)).Times(1);
    388   tab_helper().DidCommitProvisionalLoadForFrame(
    389       1, base::string16(), true, same_site_url, content::PAGE_TRANSITION_LINK,
    390       render_view_host1());
    391 }
    392 
    393 // Simulates navigations for a number of subframes, and makes sure no
    394 // CaptivePortalTabHelper function is called.
    395 TEST_F(CaptivePortalTabHelperTest, HttpsSubframe) {
    396   GURL url = GURL(kHttpsUrl);
    397   // Normal load.
    398   tab_helper().DidStartProvisionalLoadForFrame(
    399       1, -1, false, url, false, false, render_view_host1());
    400   tab_helper().DidCommitProvisionalLoadForFrame(
    401       1, base::string16(), false, url, content::PAGE_TRANSITION_LINK,
    402       render_view_host1());
    403 
    404   // Timeout.
    405   tab_helper().DidStartProvisionalLoadForFrame(
    406       2, -1, false, url, false, false, render_view_host1());
    407   tab_helper().DidFailProvisionalLoad(
    408       2, base::string16(), false, url, net::ERR_TIMED_OUT, base::string16(),
    409       render_view_host1());
    410   tab_helper().DidStartProvisionalLoadForFrame(
    411       2, -1, false, url, true, false, render_view_host1());
    412   tab_helper().DidFailProvisionalLoad(
    413       2, base::string16(), false, url, net::ERR_ABORTED, base::string16(),
    414       render_view_host1());
    415 
    416   // Abort.
    417   tab_helper().DidStartProvisionalLoadForFrame(
    418       3, -1, false, url, false, false, render_view_host1());
    419   tab_helper().DidFailProvisionalLoad(
    420       3, base::string16(), false, url, net::ERR_ABORTED, base::string16(),
    421       render_view_host1());
    422 }
    423 
    424 // Simulates a subframe erroring out at the same time as a provisional load,
    425 // but with a different error code.  Make sure the TabHelper sees the correct
    426 // error.
    427 TEST_F(CaptivePortalTabHelperTest, HttpsSubframeParallelError) {
    428   // URL used by both frames.
    429   GURL url = GURL(kHttpsUrl);
    430 
    431   int frame_id = 2;
    432   int subframe_id = 1;
    433 
    434   // Loads start.
    435   EXPECT_CALL(mock_reloader(), OnLoadStart(url.SchemeIsSecure())).Times(1);
    436   tab_helper().DidStartProvisionalLoadForFrame(
    437       frame_id, -1, true, url, false, false, render_view_host1());
    438   tab_helper().DidStartProvisionalLoadForFrame(
    439       subframe_id, frame_id, false, url, false, false, render_view_host1());
    440 
    441   // Loads return errors.
    442   tab_helper().DidFailProvisionalLoad(
    443       frame_id, base::string16(), true, url, net::ERR_UNEXPECTED,
    444       base::string16(), render_view_host1());
    445   tab_helper().DidFailProvisionalLoad(
    446       subframe_id, base::string16(), false, url, net::ERR_TIMED_OUT,
    447       base::string16(), render_view_host1());
    448 
    449   // Provisional load starts for the error pages.
    450   tab_helper().DidStartProvisionalLoadForFrame(
    451       frame_id, -1, true, url, true, false, render_view_host1());
    452   tab_helper().DidStartProvisionalLoadForFrame(
    453       subframe_id, frame_id, false, url, true, false, render_view_host1());
    454 
    455   // Error page load finishes.
    456   tab_helper().DidCommitProvisionalLoadForFrame(
    457       subframe_id, base::string16(), false, url,
    458       content::PAGE_TRANSITION_AUTO_SUBFRAME, render_view_host1());
    459   EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::ERR_UNEXPECTED)).Times(1);
    460   tab_helper().DidCommitProvisionalLoadForFrame(
    461       frame_id, base::string16(), true, url, content::PAGE_TRANSITION_LINK,
    462       render_view_host1());
    463 }
    464 
    465 // Simulates an HTTP to HTTPS redirect, which then times out.
    466 TEST_F(CaptivePortalTabHelperTest, HttpToHttpsRedirectTimeout) {
    467   GURL http_url(kHttpUrl);
    468   EXPECT_CALL(mock_reloader(), OnLoadStart(false)).Times(1);
    469   tab_helper().DidStartProvisionalLoadForFrame(
    470       1, -1, true, http_url, false, false, render_view_host1());
    471 
    472   GURL https_url(kHttpsUrl);
    473   EXPECT_CALL(mock_reloader(), OnRedirect(true)).Times(1);
    474   OnRedirect(ResourceType::MAIN_FRAME, https_url,
    475              render_view_host1()->GetProcess()->GetID());
    476 
    477   tab_helper().DidFailProvisionalLoad(
    478       1, base::string16(), true, https_url, net::ERR_TIMED_OUT,
    479       base::string16(), render_view_host1());
    480 
    481   // Provisional load starts for the error page.
    482   tab_helper().DidStartProvisionalLoadForFrame(
    483       1, -1, true, GURL(kErrorPageUrl), true, false, render_view_host1());
    484 
    485   EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::ERR_TIMED_OUT)).Times(1);
    486   tab_helper().DidCommitProvisionalLoadForFrame(
    487       1, base::string16(), true, GURL(kErrorPageUrl),
    488       content::PAGE_TRANSITION_LINK, render_view_host1());
    489 }
    490 
    491 // Simulates an HTTPS to HTTP redirect.
    492 TEST_F(CaptivePortalTabHelperTest, HttpsToHttpRedirect) {
    493   GURL https_url(kHttpsUrl);
    494   EXPECT_CALL(mock_reloader(),
    495               OnLoadStart(https_url.SchemeIsSecure())).Times(1);
    496   tab_helper().DidStartProvisionalLoadForFrame(1, -1, true, https_url, false,
    497                                                false, render_view_host1());
    498 
    499   GURL http_url(kHttpUrl);
    500   EXPECT_CALL(mock_reloader(), OnRedirect(http_url.SchemeIsSecure())).Times(1);
    501   OnRedirect(ResourceType::MAIN_FRAME, http_url,
    502              render_view_host1()->GetProcess()->GetID());
    503 
    504   EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::OK)).Times(1);
    505   tab_helper().DidCommitProvisionalLoadForFrame(
    506       1, base::string16(), true, http_url, content::PAGE_TRANSITION_LINK,
    507       render_view_host1());
    508 }
    509 
    510 // Simulates an HTTPS to HTTPS redirect.
    511 TEST_F(CaptivePortalTabHelperTest, HttpToHttpRedirect) {
    512   GURL http_url(kHttpUrl);
    513   EXPECT_CALL(mock_reloader(),
    514               OnLoadStart(http_url.SchemeIsSecure())).Times(1);
    515   tab_helper().DidStartProvisionalLoadForFrame(
    516       1, -1, true, http_url, false, false, render_view_host1());
    517 
    518   EXPECT_CALL(mock_reloader(), OnRedirect(http_url.SchemeIsSecure())).Times(1);
    519   OnRedirect(ResourceType::MAIN_FRAME, http_url,
    520              render_view_host1()->GetProcess()->GetID());
    521 
    522   EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::OK)).Times(1);
    523   tab_helper().DidCommitProvisionalLoadForFrame(
    524       1, base::string16(), true, http_url, content::PAGE_TRANSITION_LINK,
    525       render_view_host1());
    526 }
    527 
    528 // Simulates redirect of a subframe.
    529 TEST_F(CaptivePortalTabHelperTest, SubframeRedirect) {
    530   GURL http_url(kHttpUrl);
    531   EXPECT_CALL(mock_reloader(), OnLoadStart(false)).Times(1);
    532   tab_helper().DidStartProvisionalLoadForFrame(
    533       1, -1, true, http_url, false, false, render_view_host1());
    534 
    535   GURL https_url(kHttpsUrl);
    536   OnRedirect(ResourceType::SUB_FRAME, https_url,
    537              render_view_host1()->GetProcess()->GetID());
    538 
    539   EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::OK)).Times(1);
    540   tab_helper().DidCommitProvisionalLoadForFrame(
    541       1, base::string16(), true, GURL(kErrorPageUrl),
    542       content::PAGE_TRANSITION_LINK, render_view_host1());
    543 }
    544 
    545 // Simulates a redirect, for another RenderViewHost.
    546 TEST_F(CaptivePortalTabHelperTest, OtherRenderViewHostRedirect) {
    547   GURL http_url(kHttpUrl);
    548   EXPECT_CALL(mock_reloader(), OnLoadStart(false)).Times(1);
    549   tab_helper().DidStartProvisionalLoadForFrame(
    550       1, -1, true, http_url, false, false, render_view_host1());
    551 
    552   // Another RenderViewHost sees a redirect.  None of the reloader's functions
    553   // should be called.
    554   GURL https_url(kHttpsUrl);
    555   OnRedirect(ResourceType::MAIN_FRAME, https_url,
    556              render_view_host2()->GetProcess()->GetID());
    557 
    558   tab_helper().DidFailProvisionalLoad(
    559       1, base::string16(), true, https_url, net::ERR_TIMED_OUT,
    560       base::string16(), render_view_host1());
    561 
    562   // Provisional load starts for the error page.
    563   tab_helper().DidStartProvisionalLoadForFrame(
    564       1, -1, true, GURL(kErrorPageUrl), true, false, render_view_host1());
    565 
    566   EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::ERR_TIMED_OUT)).Times(1);
    567   tab_helper().DidCommitProvisionalLoadForFrame(
    568       1, base::string16(), true, GURL(kErrorPageUrl),
    569       content::PAGE_TRANSITION_LINK, render_view_host1());
    570 }
    571 
    572 TEST_F(CaptivePortalTabHelperTest, LoginTabLogin) {
    573   EXPECT_FALSE(tab_helper().IsLoginTab());
    574   SetIsLoginTab();
    575   EXPECT_TRUE(tab_helper().IsLoginTab());
    576 
    577   ObservePortalResult(captive_portal::RESULT_INTERNET_CONNECTED,
    578                       captive_portal::RESULT_INTERNET_CONNECTED);
    579   EXPECT_FALSE(tab_helper().IsLoginTab());
    580 }
    581 
    582 TEST_F(CaptivePortalTabHelperTest, LoginTabError) {
    583   EXPECT_FALSE(tab_helper().IsLoginTab());
    584 
    585   SetIsLoginTab();
    586   EXPECT_TRUE(tab_helper().IsLoginTab());
    587 
    588   ObservePortalResult(captive_portal::RESULT_INTERNET_CONNECTED,
    589                       captive_portal::RESULT_NO_RESPONSE);
    590   EXPECT_FALSE(tab_helper().IsLoginTab());
    591 }
    592 
    593 TEST_F(CaptivePortalTabHelperTest, LoginTabMultipleResultsBeforeLogin) {
    594   EXPECT_FALSE(tab_helper().IsLoginTab());
    595 
    596   SetIsLoginTab();
    597   EXPECT_TRUE(tab_helper().IsLoginTab());
    598 
    599   ObservePortalResult(captive_portal::RESULT_INTERNET_CONNECTED,
    600                       captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL);
    601   EXPECT_TRUE(tab_helper().IsLoginTab());
    602 
    603   ObservePortalResult(captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL,
    604                       captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL);
    605   EXPECT_TRUE(tab_helper().IsLoginTab());
    606 
    607   ObservePortalResult(captive_portal::RESULT_NO_RESPONSE,
    608                       captive_portal::RESULT_INTERNET_CONNECTED);
    609   EXPECT_FALSE(tab_helper().IsLoginTab());
    610 }
    611 
    612 TEST_F(CaptivePortalTabHelperTest, NoLoginTab) {
    613   EXPECT_FALSE(tab_helper().IsLoginTab());
    614 
    615   ObservePortalResult(captive_portal::RESULT_INTERNET_CONNECTED,
    616                       captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL);
    617   EXPECT_FALSE(tab_helper().IsLoginTab());
    618 
    619   ObservePortalResult(captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL,
    620                       captive_portal::RESULT_NO_RESPONSE);
    621   EXPECT_FALSE(tab_helper().IsLoginTab());
    622 
    623   ObservePortalResult(captive_portal::RESULT_NO_RESPONSE,
    624                       captive_portal::RESULT_INTERNET_CONNECTED);
    625   EXPECT_FALSE(tab_helper().IsLoginTab());
    626 }
    627