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

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: cleanup comment Created 8 years, 7 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
Property Changes:
Added: svn:eol-style
+ LF
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/browser_finder.h"
14 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
15 #include "chrome/common/chrome_notification_types.h"
16 #include "content/public/browser/navigation_controller.h"
17 #include "content/public/browser/navigation_entry.h"
18 #include "content/public/browser/notification_details.h"
19 #include "content/public/browser/notification_source.h"
20 #include "content/public/browser/web_contents.h"
21 #include "net/base/net_errors.h"
22
23 namespace captive_portal {
24
25 namespace {
26
27 // The time to wait for a slow loading SSL page before triggering a captive
28 // portal check.
29 const int kDefaultSlowSSLTimeSeconds = 30;
30
31 } // namespace
32
33 CaptivePortalTabHelper::CaptivePortalTabHelper(
34 Profile* profile,
35 content::WebContents* web_contents)
36 : ALLOW_THIS_IN_INITIALIZER_LIST(observer_(this, web_contents)),
37 profile_(profile),
38 state_(STATE_NONE),
39 provisional_main_frame_load_(false),
40 slow_ssl_load_time_(
41 base::TimeDelta::FromSeconds(kDefaultSlowSSLTimeSeconds)),
42 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
43 registrar_.Add(this,
44 chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT,
45 content::Source<Profile>(profile_));
46 }
47
48 CaptivePortalTabHelper::~CaptivePortalTabHelper() {
49 }
50
51 void CaptivePortalTabHelper::OnLoadStart(bool is_ssl) {
52 DCHECK(CalledOnValidThread());
53
54 provisional_main_frame_load_ = true;
55
56 // If this is a login tab, do nothing.
57 if (state_ == STATE_CAPTIVE_PORTAL_LOGIN_PAGE)
58 return;
59
60 // Otherwise, reset the state.
61 SetState(STATE_NONE);
62
63 // Start the slow load timer for SSL pages.
64 // TODO(mmenke): Should this look at the port instead? The reason the
65 // request never connects is because of the port, not the
66 // protocol.
67 if (is_ssl)
68 SetState(STATE_TIMER_RUNNING);
69 }
70
71 void CaptivePortalTabHelper::OnLoadCommitted(int net_error) {
72 DCHECK(CalledOnValidThread());
73
74 provisional_main_frame_load_ = false;
75
76 if (state_ == STATE_CAPTIVE_PORTAL_LOGIN_PAGE || state_ == STATE_NONE)
77 return;
78
79 // If there's no timeout error, reset the state.
80 if (net_error != net::ERR_CONNECTION_TIMED_OUT) {
81 // TODO(mmenke): If the new URL is the same as the old broken URL, and the
82 // request succeeds, should probably trigger another
83 // captive portal check.
84 SetState(STATE_NONE);
85 return;
86 }
87
88 // The page timed out before the timer triggered. This is not terribly
89 // likely, but if it does happen, the tab may have been broken by a captive
90 // portal. Go ahead and try to detect a portal now, rather than waiting for
91 // the timer.
92 if (state_ == STATE_TIMER_RUNNING) {
93 OnSlowSSLConnect();
94 return;
95 }
96
97 // If the tab needs to reload, do so asynchronously, to avoid reentrancy
98 // issues.
99 if (state_ == STATE_NEEDS_RELOAD) {
100 MessageLoop::current()->PostTask(
101 FROM_HERE,
102 base::Bind(&CaptivePortalTabHelper::ReloadTabIfNeeded,
103 weak_factory_.GetWeakPtr()));
104 }
105 }
106
107 void CaptivePortalTabHelper::OnAbort() {
108 DCHECK(CalledOnValidThread());
109
110 provisional_main_frame_load_ = false;
111 if (state_ == STATE_CAPTIVE_PORTAL_LOGIN_PAGE)
112 return;
113 SetState(STATE_NONE);
114 }
115
116 void CaptivePortalTabHelper::OnStopLoading() {
117 DCHECK(CalledOnValidThread());
118
119 // Check for captive portal when loading stops. This can't be done on
120 // DidFinishLoad in the case of captive portals that return the user to
121 // the originally requested page, as getting a 204 response does not result
122 // in a DidFinishLoad call.
123 if (state_ == STATE_CAPTIVE_PORTAL_LOGIN_PAGE)
124 CheckForCaptivePortal();
125 }
126
127 void CaptivePortalTabHelper::Observe(
128 int type,
129 const content::NotificationSource& source,
130 const content::NotificationDetails& details) {
131 DCHECK(CalledOnValidThread());
132 DCHECK_EQ(chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT, type);
133 DCHECK_EQ(profile_, content::Source<Profile>(source).ptr());
134
135 CaptivePortalService::Results* results =
136 content::Details<CaptivePortalService::Results>(details).ptr();
137
138 DCHECK(CalledOnValidThread());
139
140 if (results->result == RESULT_BEHIND_CAPTIVE_PORTAL) {
141 if (state_ == STATE_MAYBE_BROKEN_BY_PORTAL) {
142 SetState(STATE_BROKEN_BY_PORTAL);
143 MaybeOpenCaptivePortalLoginTab();
144 }
145 return;
146 }
147
148 switch (state_) {
149 case STATE_MAYBE_BROKEN_BY_PORTAL:
150 case STATE_TIMER_RUNNING:
151 // If the previous result was BEHIND_CAPTIVE_PORTAL, and the state is
152 // either STATE_MAYBE_BROKEN_BY_PORTAL or STATE_TIMER_RUNNING, reload the
153 // tab. In the latter case, the tab has yet to commit, but is an SSL
154 // page, so if the page ends up at a timeout error, it will be reloaded.
155 // If not, the state will just be reset. The helps in the case that a
156 // user tries to reload a tab, and then quickly logs in.
157 if (results->previous_result == RESULT_BEHIND_CAPTIVE_PORTAL) {
158 SetState(STATE_NEEDS_RELOAD);
159 return;
160 }
161 SetState(STATE_NONE);
162 return;
163
164 case STATE_BROKEN_BY_PORTAL:
165 // Either reload the tab now, if a connection timed out error page has
166 // already been committed, or reload it if and when a timeout commits.
167 SetState(STATE_NEEDS_RELOAD);
168 return;
169
170 case STATE_CAPTIVE_PORTAL_LOGIN_PAGE:
171 // There is no captive portal any more, so this is no longer a login tab.
172 SetState(STATE_NONE);
173 return;
174
175 case STATE_NEEDS_RELOAD:
176 case STATE_NONE:
177 // If the tab needs to reload or is in STATE_NONE, do nothing. The reload
178 // case shouldn't be very common, since it only lasts until a tab times
179 // out, but it's still possible.
180 return;
181
182 default:
183 NOTREACHED();
184 }
185 }
186
187 void CaptivePortalTabHelper::OnSlowSSLConnect() {
188 SetState(STATE_MAYBE_BROKEN_BY_PORTAL);
189 }
190
191 void CaptivePortalTabHelper::SetState(State new_state) {
192 // Stop the timer even when old and new states are the same.
193 if (state_ == STATE_TIMER_RUNNING) {
194 slow_ssl_load_timer_.Stop();
195 } else {
196 DCHECK(!slow_ssl_load_timer_.IsRunning());
197 }
198
199 // Check for unexpected state transitions.
200 switch (state_) {
201 case STATE_NONE:
202 DCHECK(new_state == STATE_NONE ||
203 new_state == STATE_TIMER_RUNNING ||
204 new_state == STATE_CAPTIVE_PORTAL_LOGIN_PAGE);
205 break;
206 case STATE_CAPTIVE_PORTAL_LOGIN_PAGE:
207 DCHECK_EQ(STATE_NONE, new_state);
208 break;
209 case STATE_TIMER_RUNNING:
210 DCHECK(new_state == STATE_NONE ||
211 new_state == STATE_MAYBE_BROKEN_BY_PORTAL ||
212 new_state == STATE_NEEDS_RELOAD);
213 break;
214 case STATE_MAYBE_BROKEN_BY_PORTAL:
215 DCHECK(new_state == STATE_NONE ||
216 new_state == STATE_BROKEN_BY_PORTAL ||
217 new_state == STATE_NEEDS_RELOAD);
218 break;
219 case STATE_BROKEN_BY_PORTAL:
220 DCHECK(new_state == STATE_NONE ||
221 new_state == STATE_NEEDS_RELOAD);
222 break;
223 case STATE_NEEDS_RELOAD:
224 DCHECK_EQ(STATE_NONE, new_state);
225 break;
226 default:
227 NOTREACHED();
228 break;
229 };
230
231 state_ = new_state;
232
233 switch (state_) {
234 case STATE_TIMER_RUNNING:
235 slow_ssl_load_timer_.Start(
236 FROM_HERE,
237 slow_ssl_load_time_,
238 this,
239 &CaptivePortalTabHelper::OnSlowSSLConnect);
240 break;
241
242 case STATE_MAYBE_BROKEN_BY_PORTAL:
243 CheckForCaptivePortal();
244 break;
245
246 case STATE_NEEDS_RELOAD:
247 // Try to reload the tab now.
248 ReloadTabIfNeeded();
249 break;
250
251 default:
252 break;
253 }
254 }
255
256 void CaptivePortalTabHelper::ReloadTabIfNeeded() {
257 // If there's still a provisional load going, or the page no longer needs
258 // to be reloaded, due to a new navigation, do nothing.
259 if (state_ != STATE_NEEDS_RELOAD || provisional_main_frame_load_)
260 return;
261 SetState(STATE_NONE);
262 ReloadTab();
263 }
264
265 void CaptivePortalTabHelper::ReloadTab() {
266 content::NavigationController* controller =
267 &observer_.web_contents()->GetController();
268 if (!controller->GetActiveEntry()->GetHasPostData())
269 observer_.web_contents()->GetController().Reload(true);
270 }
271
272 void CaptivePortalTabHelper::MaybeOpenCaptivePortalLoginTab() {
273 Browser* browser = browser::FindTabbedBrowser(profile_, true);
274 // If the Profile doesn't have a tabbed browser window open, do nothing.
275 if (!browser)
276 return;
277
278 // Check if the Profile's topmost browser window already has a login tab.
279 // If so, do nothing.
280 // TODO(mmenke): Consider focusing that tab, at least if this is the tab
281 // helper for the currently active tab for the profile.
282 for (int i = 0; i < browser->tab_count(); ++i) {
283 TabContentsWrapper* tab_contents_wrapper =
284 browser->GetTabContentsWrapperAt(i);
285 if (tab_contents_wrapper->captive_portal_tab_helper()->state() ==
286 CaptivePortalTabHelper::STATE_CAPTIVE_PORTAL_LOGIN_PAGE) {
287 return;
288 }
289 }
290
291 // Otherwise, open a login tab. Only end up here when a captive portal result
292 // was received, so it's safe to assume |profile_| has a CaptivePortalService.
293 TabContentsWrapper* tab_contents_wrapper =
294 browser->AddSelectedTabWithURL(
295 CaptivePortalServiceFactory::GetForProfile(profile_)->test_url(),
296 content::PAGE_TRANSITION_TYPED);
297 tab_contents_wrapper->captive_portal_tab_helper()->SetState(
298 STATE_CAPTIVE_PORTAL_LOGIN_PAGE);
299 }
300
301 void CaptivePortalTabHelper::CheckForCaptivePortal() {
302 CaptivePortalService* service =
303 CaptivePortalServiceFactory::GetForProfile(profile_);
304 if (service)
305 service->DetectCaptivePortal();
306 }
307
308 } // namespace captive_portal
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698