Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(477)

Side by Side Diff: chrome/browser/captive_portal/captive_portal_tab_helper.cc

Issue 10020051: Open a login tab on captive portal detection on SSL loads. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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/bind.h"
8 #include "base/message_loop.h"
9 #include "chrome/browser/captive_portal/captive_portal_service.h"
10 #include "chrome/browser/captive_portal/captive_portal_service_factory.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/ui/browser.h"
13 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
14 #include "chrome/common/chrome_notification_types.h"
15 #include "content/public/browser/navigation_controller.h"
16 #include "content/public/browser/notification_details.h"
17 #include "content/public/browser/notification_source.h"
18 #include "content/public/browser/web_contents.h"
19 #include "net/base/net_errors.h"
20
21 namespace captive_portal {
22
23 namespace {
24
25 // The time we wait for a slow loading SSL page before trigginer a captive
cbentzel 2012/04/18 15:12:48 Nit: trigginer
mmenke 2012/04/18 19:15:46 Fixed.
26 // portal check.
27 static int kDefaultSlowSSLTime = 30;
cbentzel 2012/04/18 15:57:23 Nit: kDefaultSlowSSLTimeSeconds since this does no
mmenke 2012/04/18 19:15:46 Done.
28
29 } // namespace
30
31 CaptivePortalTabHelper::~CaptivePortalTabHelper() {
32 }
33
34 // static
35 CaptivePortalTabHelper* CaptivePortalTabHelper::Create(
36 TabContentsWrapper* tab_contents) {
37 return new CaptivePortalTabHelper(
38 tab_contents->profile(),
cbentzel 2012/04/18 15:57:23 Nit: is this correct indent? Should just be 4 from
mmenke 2012/04/18 19:15:46 Removed the function.
39 tab_contents->web_contents());
40 }
41
42 CaptivePortalTabHelper::CaptivePortalTabHelper(
43 Profile* profile,
44 content::WebContents* web_contents)
45 : ALLOW_THIS_IN_INITIALIZER_LIST(observer_(this, web_contents)),
46 profile_(profile),
47 state_(STATE_NONE),
48 provisional_main_frame_load_(false),
49 slow_ssl_load_time_(base::TimeDelta::FromSeconds(kDefaultSlowSSLTime)),
50 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
51 registrar_.Add(this,
52 chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT,
53 content::Source<Profile>(profile_));
54 }
55
56 void CaptivePortalTabHelper::OnLoadStart(bool is_ssl) {
57 DCHECK(CalledOnValidThread());
58
59 provisional_main_frame_load_ = true;
60
61 // Don't change state when error pages start loading. If we're going to
62 // reload, we want to reload only after the error page has been committed.
63 // If this is a login tab, also do nothing.
64 if (state_ == STATE_CAPTIVE_PORTAL_LOGIN_PAGE)
65 return;
66
67 // Otherwise, reset the state, as we're at a new page.
68 SetState(STATE_NONE);
69
70 // Start the slow load timer for SSL pages.
71 // TODO(mmenke): Should we be looking at the port instead? The reason the
72 // request never connects is because of the port, not the
73 // protocol.
74 if (is_ssl)
75 SetState(STATE_TIMER_RUNNING);
76 }
77
78 void CaptivePortalTabHelper::OnLoadCommitted(int net_error) {
79 DCHECK(CalledOnValidThread());
80
81 provisional_main_frame_load_ = false;
82
83 if (state_ == STATE_CAPTIVE_PORTAL_LOGIN_PAGE || state_ == STATE_NONE)
84 return;
85
86 // If we don't have a timeout error, reset the state.
87 if (net_error != net::ERR_CONNECTION_TIMED_OUT) {
88 // TODO(mmenke): If the new URL is the same as the old broken URL, and the
89 // request succeeds, we should probably trigger another
90 // captive portal check.
91 SetState(STATE_NONE);
92 return;
93 }
94
95 // We timed out before the timer expired. This is not terribly likely, but
96 // if it does happen, we may have been broken by a captive portal. Go ahead
97 // and start the check now, rather than waiting for the timer.
98 if (state_ == STATE_TIMER_RUNNING) {
99 OnSlowSSLConnect();
100 return;
101 }
102
103 // If we need to reload, do so asynchronously, since we're in a callback from
104 // the WebContents.
105 if (state_ == STATE_NEEDS_RELOAD) {
106 // If we need to reload the page, do so asynchronously.
107 MessageLoop::current()->PostTask(
cbentzel 2012/04/18 15:12:48 I'm a little concerned here about doing unnecessar
mmenke 2012/04/18 19:15:46 We only get here when we commit with a connection
108 FROM_HERE,
109 base::Bind(&CaptivePortalTabHelper::ReloadTabIfNeeded,
110 weak_factory_.GetWeakPtr()));
111 }
112 }
113
114 void CaptivePortalTabHelper::OnAbort() {
115 DCHECK(CalledOnValidThread());
116
117 provisional_main_frame_load_ = false;
118 if (state_ == STATE_CAPTIVE_PORTAL_LOGIN_PAGE)
119 return;
120 SetState(STATE_NONE);
121 }
122
123 void CaptivePortalTabHelper::OnStopLoading() {
124 DCHECK(CalledOnValidThread());
125
126 // Check for captive portal when loading stops. This can't be done on
127 // DidFinishLoad in the case of captive portals that return the user to
128 // the originally requested page, as getting a 204 response does not result
129 // in a DidFinishLoad call.
130 if (state_ == STATE_CAPTIVE_PORTAL_LOGIN_PAGE)
131 CheckForCaptivePortal();
132 }
133
134 void CaptivePortalTabHelper::Observe(
135 int type,
136 const content::NotificationSource& source,
137 const content::NotificationDetails& details) {
138 DCHECK(CalledOnValidThread());
139 DCHECK_EQ(chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT, type);
140 DCHECK_EQ(profile_, content::Source<Profile>(source).ptr());
141
142 CaptivePortalService::Results* results =
143 content::Details<CaptivePortalService::Results>(details).ptr();
144
145 DCHECK(CalledOnValidThread());
146
147 if (results->result == RESULT_BEHIND_CAPTIVE_PORTAL) {
148 if (state_ == STATE_MAYBE_BROKEN_BY_PORTAL) {
149 SetState(STATE_BROKEN_BY_PORTAL);
150 MaybeOpenCaptivePortalLoginTab();
151 }
152 return;
153 }
154
155 switch (state_) {
156 case STATE_MAYBE_BROKEN_BY_PORTAL:
157 case STATE_TIMER_RUNNING:
158 // If the previous result was BEHIND_CAPTIVE_PORTAL, and we're either in
159 // STATE_MAYBE_BROKEN_BY_PORTAL or STATE_TIMER_RUNNING, reload the tab.
160 // In the latter case, the tab has yet to commit, but is an SSL page, so
161 // if the page ends up at a timeout error, we'll reload it. If not,
162 // just reset the state. The helps in the case that a user tries to
163 // reload a tab, and then quickly logs in.
164 if (results->previous_result == RESULT_BEHIND_CAPTIVE_PORTAL) {
165 SetState(STATE_NEEDS_RELOAD);
166 return;
167 }
168 SetState(STATE_NONE);
169 return;
170
171 case STATE_BROKEN_BY_PORTAL:
172 // If we don't have a provisional load going, and we were broken by a
173 // captive portal, reload the page. If we don't reload the tab, we keep
174 // the current state and reload when the current load finally times out,
175 // assuming it does so.
176 SetState(STATE_NEEDS_RELOAD);
177 return;
178
179 case STATE_CAPTIVE_PORTAL_LOGIN_PAGE:
180 // There is no captive portal any more, so we're no longer a login tab.
181 SetState(STATE_NONE);
cbentzel 2012/04/18 15:12:48 Should this politely close itself, or are there co
mmenke 2012/04/18 19:15:46 The user could have logged in from another tab, so
182 return;
183
184 case STATE_NEEDS_RELOAD:
185 case STATE_NONE:
186 // If the tab needs to reload or is in STATE_NONE, do nothing. The reload
187 // case shouldn't be very common, since it only lasts until a tab times
188 // out, but it's still possible.
189 return;
190
191 default:
192 NOTREACHED();
193 }
194 }
195
196 void CaptivePortalTabHelper::OnSlowSSLConnect() {
197 SetState(STATE_MAYBE_BROKEN_BY_PORTAL);
198 }
199
200 void CaptivePortalTabHelper::SetState(State new_state) {
201 // Stop the timer even when old and new states are the same.
202 slow_ssl_load_timer_.Stop();
cbentzel 2012/04/18 15:12:48 Do we only need to do this if state_ == STATE_TIME
mmenke 2012/04/18 19:15:46 Done.
203
cbentzel 2012/04/18 15:12:48 Would it make sense to add DCHECKs here about vali
mmenke 2012/04/18 19:15:46 Done. Valid transitions are: From anything to NO
204 state_ = new_state;
205
206 switch (state_) {
207 case STATE_TIMER_RUNNING:
208 slow_ssl_load_timer_.Start(
209 FROM_HERE,
210 slow_ssl_load_time_,
211 this,
212 &CaptivePortalTabHelper::OnSlowSSLConnect);
213 break;
214
215 case STATE_MAYBE_BROKEN_BY_PORTAL:
216 CheckForCaptivePortal();
217 break;
218
219 case STATE_NEEDS_RELOAD:
220 // Try to reload the tab now.
221 ReloadTabIfNeeded();
222 break;
223
224 default:
225 break;
226 }
227 }
228
229 void CaptivePortalTabHelper::ReloadTabIfNeeded() {
230 // If there's still a provisional load going, or we're no longer in
231 // STATE_NEEDS_RELOAD, do nothing.
232 if (state_ != STATE_NEEDS_RELOAD || provisional_main_frame_load_)
233 return;
234 SetState(STATE_NONE);
235 ReloadTab();
236 }
237
238 void CaptivePortalTabHelper::ReloadTab() {
239 observer_.web_contents()->GetController().Reload(true);
cbentzel 2012/04/18 15:12:48 Do you want to have the user manually reload if th
mmenke 2012/04/18 19:15:46 Since we failed to commit, we'll always reload the
240 }
241
242 void CaptivePortalTabHelper::MaybeOpenCaptivePortalLoginTab() {
cbentzel 2012/04/18 15:12:48 I'm wondering if this should move to the service i
mmenke 2012/04/18 19:15:46 I've gone back and forth on this. Currently, the
cbentzel 2012/04/18 21:12:37 That's a good argument for why this is here.
243 Browser* browser = Browser::GetTabbedBrowser(profile_, true);
244 // If the Profile doesn't have a tabbed browser window open, do nothing.
245 if (!browser)
246 return;
247
248 // Check if the Profile's topmost browser window already has a login tab.
249 // If so, do nothing.
250 // TODO(mmenke): Consider focusing that tab, at least if we're already
251 // the active tab and in the same Browser window.
252 for (int i = 0; i < browser->tab_count(); ++i) {
253 TabContentsWrapper* tab_contents_wrapper =
254 browser->GetTabContentsWrapperAt(i);
255 if (tab_contents_wrapper->captive_portal_tab_helper()->state() ==
256 CaptivePortalTabHelper::STATE_CAPTIVE_PORTAL_LOGIN_PAGE) {
257 return;
258 }
259 }
260
261 // Otherwise, open a login tab.
262 TabContentsWrapper* tab_contents_wrapper =
263 browser->AddSelectedTabWithURL(
264 CaptivePortalServiceFactory::GetForProfile(profile_)->test_url(),
265 content::PAGE_TRANSITION_TYPED);
cbentzel 2012/04/18 15:57:23 Should we add a new page transition type here? Do
mmenke 2012/04/18 19:15:46 It would be great if the page didn't show up in th
cbentzel 2012/04/18 21:12:37 OK.
266 tab_contents_wrapper->captive_portal_tab_helper()->SetState(
267 STATE_CAPTIVE_PORTAL_LOGIN_PAGE);
268 }
269
270 void CaptivePortalTabHelper::CheckForCaptivePortal() {
271 CaptivePortalServiceFactory::GetForProfile(profile_)->CheckForCaptivePortal();
cbentzel 2012/04/18 14:36:31 Is this guaranteed to always exist? Probably safer
mmenke 2012/04/18 14:40:41 Yes, it's guaranteed to exist. I'll go ahead and
272 }
273
274 } // namespace captive_portal
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698