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