OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/ui/login/login_prompt.h" | 5 #include "chrome/browser/ui/login/login_prompt.h" |
6 | 6 |
7 #include <vector> | 7 #include <vector> |
8 | 8 |
9 #include "app/l10n_util.h" | 9 #include "app/l10n_util.h" |
10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
109 | 109 |
110 TabContents* LoginHandler::GetTabContentsForLogin() const { | 110 TabContents* LoginHandler::GetTabContentsForLogin() const { |
111 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 111 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
112 | 112 |
113 return tab_util::GetTabContentsByID(render_process_host_id_, | 113 return tab_util::GetTabContentsByID(render_process_host_id_, |
114 tab_contents_id_); | 114 tab_contents_id_); |
115 } | 115 } |
116 | 116 |
117 void LoginHandler::SetAuth(const std::wstring& username, | 117 void LoginHandler::SetAuth(const std::wstring& username, |
118 const std::wstring& password) { | 118 const std::wstring& password) { |
119 if (WasAuthHandled(true)) | 119 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 120 |
| 121 if (TestAndSetAuthHandled()) |
120 return; | 122 return; |
121 | 123 |
122 // Tell the password manager the credentials were submitted / accepted. | 124 // Tell the password manager the credentials were submitted / accepted. |
123 if (password_manager_) { | 125 if (password_manager_) { |
124 password_form_.username_value = WideToUTF16Hack(username); | 126 password_form_.username_value = WideToUTF16Hack(username); |
125 password_form_.password_value = WideToUTF16Hack(password); | 127 password_form_.password_value = WideToUTF16Hack(password); |
126 password_manager_->ProvisionallySavePassword(password_form_); | 128 password_manager_->ProvisionallySavePassword(password_form_); |
127 } | 129 } |
128 | 130 |
| 131 // Calling NotifyAuthSupplied() directly instead of posting a task |
| 132 // allows other LoginHandler instances to queue their |
| 133 // CloseContentsDeferred() before ours. Closing dialogs in the |
| 134 // opposite order as they were created avoids races where remaining |
| 135 // dialogs in the same tab may be briefly displayed to the user |
| 136 // before they are removed. |
| 137 NotifyAuthSupplied(username, password); |
| 138 |
129 BrowserThread::PostTask( | 139 BrowserThread::PostTask( |
130 BrowserThread::UI, FROM_HERE, | 140 BrowserThread::UI, FROM_HERE, |
131 NewRunnableMethod(this, &LoginHandler::CloseContentsDeferred)); | 141 NewRunnableMethod(this, &LoginHandler::CloseContentsDeferred)); |
132 BrowserThread::PostTask( | 142 BrowserThread::PostTask( |
133 BrowserThread::UI, FROM_HERE, | |
134 NewRunnableMethod( | |
135 this, &LoginHandler::NotifyAuthSupplied, username, password)); | |
136 BrowserThread::PostTask( | |
137 BrowserThread::IO, FROM_HERE, | 143 BrowserThread::IO, FROM_HERE, |
138 NewRunnableMethod( | 144 NewRunnableMethod( |
139 this, &LoginHandler::SetAuthDeferred, username, password)); | 145 this, &LoginHandler::SetAuthDeferred, username, password)); |
140 } | 146 } |
141 | 147 |
142 void LoginHandler::CancelAuth() { | 148 void LoginHandler::CancelAuth() { |
143 if (WasAuthHandled(true)) | 149 if (TestAndSetAuthHandled()) |
144 return; | 150 return; |
145 | 151 |
| 152 // Similar to how we deal with notifications above in SetAuth() |
| 153 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { |
| 154 NotifyAuthCancelled(); |
| 155 } else { |
| 156 BrowserThread::PostTask( |
| 157 BrowserThread::UI, FROM_HERE, |
| 158 NewRunnableMethod(this, &LoginHandler::NotifyAuthCancelled)); |
| 159 } |
| 160 |
146 BrowserThread::PostTask( | 161 BrowserThread::PostTask( |
147 BrowserThread::UI, FROM_HERE, | 162 BrowserThread::UI, FROM_HERE, |
148 NewRunnableMethod(this, &LoginHandler::CloseContentsDeferred)); | 163 NewRunnableMethod(this, &LoginHandler::CloseContentsDeferred)); |
149 BrowserThread::PostTask( | 164 BrowserThread::PostTask( |
150 BrowserThread::UI, FROM_HERE, | |
151 NewRunnableMethod(this, &LoginHandler::NotifyAuthCancelled)); | |
152 BrowserThread::PostTask( | |
153 BrowserThread::IO, FROM_HERE, | 165 BrowserThread::IO, FROM_HERE, |
154 NewRunnableMethod(this, &LoginHandler::CancelAuthDeferred)); | 166 NewRunnableMethod(this, &LoginHandler::CancelAuthDeferred)); |
155 } | 167 } |
156 | 168 |
157 void LoginHandler::OnRequestCancelled() { | 169 void LoginHandler::OnRequestCancelled() { |
158 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)) << | 170 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)) << |
159 "Why is OnRequestCancelled called from the UI thread?"; | 171 "Why is OnRequestCancelled called from the UI thread?"; |
160 | 172 |
161 // Reference is no longer valid. | 173 // Reference is no longer valid. |
162 request_ = NULL; | 174 request_ = NULL; |
(...skipping 26 matching lines...) Expand all Loading... |
189 const NotificationSource& source, | 201 const NotificationSource& source, |
190 const NotificationDetails& details) { | 202 const NotificationDetails& details) { |
191 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 203 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
192 DCHECK(type == NotificationType::AUTH_SUPPLIED || | 204 DCHECK(type == NotificationType::AUTH_SUPPLIED || |
193 type == NotificationType::AUTH_CANCELLED); | 205 type == NotificationType::AUTH_CANCELLED); |
194 | 206 |
195 TabContents* requesting_contents = GetTabContentsForLogin(); | 207 TabContents* requesting_contents = GetTabContentsForLogin(); |
196 if (!requesting_contents) | 208 if (!requesting_contents) |
197 return; | 209 return; |
198 | 210 |
199 NavigationController* this_controller = &requesting_contents->controller(); | 211 // Break out early if we aren't interested in the notification. |
200 NavigationController* that_controller = | 212 if (WasAuthHandled()) |
201 Source<NavigationController>(source).ptr(); | |
202 | |
203 // Only handle notifications from other handlers. | |
204 if (this_controller == that_controller) | |
205 return; | 213 return; |
206 | 214 |
207 LoginNotificationDetails* login_details = | 215 LoginNotificationDetails* login_details = |
208 Details<LoginNotificationDetails>(details).ptr(); | 216 Details<LoginNotificationDetails>(details).ptr(); |
209 | 217 |
| 218 // WasAuthHandled() should always test positive before we publish |
| 219 // AUTH_SUPPLIED or AUTH_CANCELLED notifications. |
| 220 DCHECK(login_details->handler() != this); |
| 221 |
210 // Only handle notification for the identical auth info. | 222 // Only handle notification for the identical auth info. |
211 if (*login_details->handler()->auth_info() != *auth_info()) | 223 if (*login_details->handler()->auth_info() != *auth_info()) |
212 return; | 224 return; |
213 | 225 |
214 // Set or cancel the auth in this handler. | 226 // Set or cancel the auth in this handler. |
215 if (type == NotificationType::AUTH_SUPPLIED) { | 227 if (type == NotificationType::AUTH_SUPPLIED) { |
216 AuthSuppliedLoginNotificationDetails* supplied_details = | 228 AuthSuppliedLoginNotificationDetails* supplied_details = |
217 Details<AuthSuppliedLoginNotificationDetails>(details).ptr(); | 229 Details<AuthSuppliedLoginNotificationDetails>(details).ptr(); |
218 SetAuth(supplied_details->username(), supplied_details->password()); | 230 SetAuth(supplied_details->username(), supplied_details->password()); |
219 } else { | 231 } else { |
220 DCHECK(type == NotificationType::AUTH_CANCELLED); | 232 DCHECK(type == NotificationType::AUTH_CANCELLED); |
221 CancelAuth(); | 233 CancelAuth(); |
222 } | 234 } |
223 } | 235 } |
224 | 236 |
225 void LoginHandler::SetModel(LoginModel* model) { | 237 void LoginHandler::SetModel(LoginModel* model) { |
226 if (login_model_) | 238 if (login_model_) |
227 login_model_->SetObserver(NULL); | 239 login_model_->SetObserver(NULL); |
228 login_model_ = model; | 240 login_model_ = model; |
229 if (login_model_) | 241 if (login_model_) |
230 login_model_->SetObserver(this); | 242 login_model_->SetObserver(this); |
231 } | 243 } |
232 | 244 |
233 void LoginHandler::SetDialog(ConstrainedWindow* dialog) { | 245 void LoginHandler::SetDialog(ConstrainedWindow* dialog) { |
234 dialog_ = dialog; | 246 dialog_ = dialog; |
235 } | 247 } |
236 | 248 |
237 void LoginHandler::NotifyAuthNeeded() { | 249 void LoginHandler::NotifyAuthNeeded() { |
238 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 250 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
239 if (WasAuthHandled(false)) | 251 if (WasAuthHandled()) |
240 return; | 252 return; |
241 | 253 |
242 TabContents* requesting_contents = GetTabContentsForLogin(); | 254 TabContents* requesting_contents = GetTabContentsForLogin(); |
243 if (!requesting_contents) | 255 if (!requesting_contents) |
244 return; | 256 return; |
245 | 257 |
246 NotificationService* service = NotificationService::current(); | 258 NotificationService* service = NotificationService::current(); |
247 NavigationController* controller = &requesting_contents->controller(); | 259 NavigationController* controller = &requesting_contents->controller(); |
248 LoginNotificationDetails details(this); | 260 LoginNotificationDetails details(this); |
249 | 261 |
250 service->Notify(NotificationType::AUTH_NEEDED, | 262 service->Notify(NotificationType::AUTH_NEEDED, |
251 Source<NavigationController>(controller), | 263 Source<NavigationController>(controller), |
252 Details<LoginNotificationDetails>(&details)); | 264 Details<LoginNotificationDetails>(&details)); |
253 } | 265 } |
254 | 266 |
255 void LoginHandler::NotifyAuthCancelled() { | 267 void LoginHandler::NotifyAuthCancelled() { |
256 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 268 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
257 DCHECK(WasAuthHandled(false)); | 269 DCHECK(WasAuthHandled()); |
258 | 270 |
259 TabContents* requesting_contents = GetTabContentsForLogin(); | 271 TabContents* requesting_contents = GetTabContentsForLogin(); |
260 if (!requesting_contents) | 272 if (!requesting_contents) |
261 return; | 273 return; |
262 | 274 |
263 NotificationService* service = NotificationService::current(); | 275 NotificationService* service = NotificationService::current(); |
264 NavigationController* controller = &requesting_contents->controller(); | 276 NavigationController* controller = &requesting_contents->controller(); |
265 LoginNotificationDetails details(this); | 277 LoginNotificationDetails details(this); |
266 | 278 |
267 service->Notify(NotificationType::AUTH_CANCELLED, | 279 service->Notify(NotificationType::AUTH_CANCELLED, |
268 Source<NavigationController>(controller), | 280 Source<NavigationController>(controller), |
269 Details<LoginNotificationDetails>(&details)); | 281 Details<LoginNotificationDetails>(&details)); |
270 } | 282 } |
271 | 283 |
272 void LoginHandler::NotifyAuthSupplied(const std::wstring& username, | 284 void LoginHandler::NotifyAuthSupplied(const std::wstring& username, |
273 const std::wstring& password) { | 285 const std::wstring& password) { |
274 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 286 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
275 DCHECK(WasAuthHandled(false)); | 287 DCHECK(WasAuthHandled()); |
276 | 288 |
277 TabContents* requesting_contents = GetTabContentsForLogin(); | 289 TabContents* requesting_contents = GetTabContentsForLogin(); |
278 if (!requesting_contents) | 290 if (!requesting_contents) |
279 return; | 291 return; |
280 | 292 |
281 NotificationService* service = NotificationService::current(); | 293 NotificationService* service = NotificationService::current(); |
282 NavigationController* controller = &requesting_contents->controller(); | 294 NavigationController* controller = &requesting_contents->controller(); |
283 AuthSuppliedLoginNotificationDetails details(this, username, password); | 295 AuthSuppliedLoginNotificationDetails details(this, username, password); |
284 | 296 |
285 service->Notify(NotificationType::AUTH_SUPPLIED, | 297 service->Notify(NotificationType::AUTH_SUPPLIED, |
286 Source<NavigationController>(controller), | 298 Source<NavigationController>(controller), |
287 Details<AuthSuppliedLoginNotificationDetails>(&details)); | 299 Details<AuthSuppliedLoginNotificationDetails>(&details)); |
288 } | 300 } |
289 | 301 |
290 void LoginHandler::ReleaseSoon() { | 302 void LoginHandler::ReleaseSoon() { |
291 if (!WasAuthHandled(true)) { | 303 if (!TestAndSetAuthHandled()) { |
292 BrowserThread::PostTask( | 304 BrowserThread::PostTask( |
293 BrowserThread::IO, FROM_HERE, | 305 BrowserThread::IO, FROM_HERE, |
294 NewRunnableMethod(this, &LoginHandler::CancelAuthDeferred)); | 306 NewRunnableMethod(this, &LoginHandler::CancelAuthDeferred)); |
295 BrowserThread::PostTask( | 307 BrowserThread::PostTask( |
296 BrowserThread::UI, FROM_HERE, | 308 BrowserThread::UI, FROM_HERE, |
297 NewRunnableMethod(this, &LoginHandler::NotifyAuthCancelled)); | 309 NewRunnableMethod(this, &LoginHandler::NotifyAuthCancelled)); |
298 } | 310 } |
299 | 311 |
300 BrowserThread::PostTask( | 312 BrowserThread::PostTask( |
301 BrowserThread::UI, FROM_HERE, | 313 BrowserThread::UI, FROM_HERE, |
302 NewRunnableMethod(this, &LoginHandler::RemoveObservers)); | 314 NewRunnableMethod(this, &LoginHandler::RemoveObservers)); |
303 | 315 |
304 // Delete this object once all InvokeLaters have been called. | 316 // Delete this object once all InvokeLaters have been called. |
305 BrowserThread::ReleaseSoon(BrowserThread::IO, FROM_HERE, this); | 317 BrowserThread::ReleaseSoon(BrowserThread::IO, FROM_HERE, this); |
306 } | 318 } |
307 | 319 |
308 // Returns whether authentication had been handled (SetAuth or CancelAuth). | 320 // Returns whether authentication had been handled (SetAuth or CancelAuth). |
309 // If |set_handled| is true, it will mark authentication as handled. | 321 bool LoginHandler::WasAuthHandled() const { |
310 bool LoginHandler::WasAuthHandled(bool set_handled) { | |
311 AutoLock lock(handled_auth_lock_); | 322 AutoLock lock(handled_auth_lock_); |
312 bool was_handled = handled_auth_; | 323 bool was_handled = handled_auth_; |
313 if (set_handled) | |
314 handled_auth_ = true; | |
315 return was_handled; | 324 return was_handled; |
316 } | 325 } |
317 | 326 |
| 327 // Marks authentication as handled and returns the previous handled state. |
| 328 bool LoginHandler::TestAndSetAuthHandled() { |
| 329 AutoLock lock(handled_auth_lock_); |
| 330 bool was_handled = handled_auth_; |
| 331 handled_auth_ = true; |
| 332 return was_handled; |
| 333 } |
| 334 |
318 // Calls SetAuth from the IO loop. | 335 // Calls SetAuth from the IO loop. |
319 void LoginHandler::SetAuthDeferred(const std::wstring& username, | 336 void LoginHandler::SetAuthDeferred(const std::wstring& username, |
320 const std::wstring& password) { | 337 const std::wstring& password) { |
321 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 338 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
322 | 339 |
323 if (request_) { | 340 if (request_) { |
324 request_->SetAuth(WideToUTF16Hack(username), WideToUTF16Hack(password)); | 341 request_->SetAuth(WideToUTF16Hack(username), WideToUTF16Hack(password)); |
325 ResetLoginHandlerForRequest(request_); | 342 ResetLoginHandlerForRequest(request_); |
326 } | 343 } |
327 } | 344 } |
(...skipping 30 matching lines...) Expand all Loading... |
358 LoginDialogTask(const GURL& request_url, | 375 LoginDialogTask(const GURL& request_url, |
359 net::AuthChallengeInfo* auth_info, | 376 net::AuthChallengeInfo* auth_info, |
360 LoginHandler* handler) | 377 LoginHandler* handler) |
361 : request_url_(request_url), auth_info_(auth_info), handler_(handler) { | 378 : request_url_(request_url), auth_info_(auth_info), handler_(handler) { |
362 } | 379 } |
363 virtual ~LoginDialogTask() { | 380 virtual ~LoginDialogTask() { |
364 } | 381 } |
365 | 382 |
366 void Run() { | 383 void Run() { |
367 TabContents* parent_contents = handler_->GetTabContentsForLogin(); | 384 TabContents* parent_contents = handler_->GetTabContentsForLogin(); |
368 if (!parent_contents) { | 385 if (!parent_contents || handler_->WasAuthHandled()) { |
369 // The request may have been cancelled, or it may be for a renderer | 386 // The request may have been cancelled, or it may be for a renderer |
370 // not hosted by a tab (e.g. an extension). Cancel just in case | 387 // not hosted by a tab (e.g. an extension). Cancel just in case |
371 // (cancelling twice is a no-op). | 388 // (cancelling twice is a no-op). |
372 handler_->CancelAuth(); | 389 handler_->CancelAuth(); |
373 return; | 390 return; |
374 } | 391 } |
375 | 392 |
376 // Tell the password manager to look for saved passwords. | 393 // Tell the password manager to look for saved passwords. |
377 TabContentsWrapper** wrapper = | 394 TabContentsWrapper** wrapper = |
378 TabContentsWrapper::property_accessor()->GetProperty( | 395 TabContentsWrapper::property_accessor()->GetProperty( |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
446 // Public API | 463 // Public API |
447 | 464 |
448 LoginHandler* CreateLoginPrompt(net::AuthChallengeInfo* auth_info, | 465 LoginHandler* CreateLoginPrompt(net::AuthChallengeInfo* auth_info, |
449 net::URLRequest* request) { | 466 net::URLRequest* request) { |
450 LoginHandler* handler = LoginHandler::Create(auth_info, request); | 467 LoginHandler* handler = LoginHandler::Create(auth_info, request); |
451 BrowserThread::PostTask( | 468 BrowserThread::PostTask( |
452 BrowserThread::UI, FROM_HERE, new LoginDialogTask( | 469 BrowserThread::UI, FROM_HERE, new LoginDialogTask( |
453 request->url(), auth_info, handler)); | 470 request->url(), auth_info, handler)); |
454 return handler; | 471 return handler; |
455 } | 472 } |
OLD | NEW |