Index: chrome/browser/ui/login/login_prompt.cc |
diff --git a/chrome/browser/ui/login/login_prompt.cc b/chrome/browser/ui/login/login_prompt.cc |
index 40cc66a1475a651e98c76d1b5241f0f4abbc7f3a..e58d53a677b77ce891d553fbd1910917ed37299b 100644 |
--- a/chrome/browser/ui/login/login_prompt.cc |
+++ b/chrome/browser/ui/login/login_prompt.cc |
@@ -116,7 +116,9 @@ TabContents* LoginHandler::GetTabContentsForLogin() const { |
void LoginHandler::SetAuth(const std::wstring& username, |
const std::wstring& password) { |
- if (WasAuthHandled(true)) |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ |
+ if (TestAndSetAuthHandled()) |
return; |
// Tell the password manager the credentials were submitted / accepted. |
@@ -126,30 +128,40 @@ void LoginHandler::SetAuth(const std::wstring& username, |
password_manager_->ProvisionallySavePassword(password_form_); |
} |
+ // Calling NotifyAuthSupplied() directly instead of posting a task |
+ // allows other LoginHandler instances to queue their |
+ // CloseContentsDeferred() before ours. Closing dialogs in the |
+ // opposite order as they were created avoids races where remaining |
+ // dialogs in the same tab may be briefly displayed to the user |
+ // before they are removed. |
+ NotifyAuthSupplied(username, password); |
+ |
BrowserThread::PostTask( |
BrowserThread::UI, FROM_HERE, |
NewRunnableMethod(this, &LoginHandler::CloseContentsDeferred)); |
BrowserThread::PostTask( |
- BrowserThread::UI, FROM_HERE, |
- NewRunnableMethod( |
- this, &LoginHandler::NotifyAuthSupplied, username, password)); |
- BrowserThread::PostTask( |
BrowserThread::IO, FROM_HERE, |
NewRunnableMethod( |
this, &LoginHandler::SetAuthDeferred, username, password)); |
} |
void LoginHandler::CancelAuth() { |
- if (WasAuthHandled(true)) |
+ if (TestAndSetAuthHandled()) |
return; |
+ // Similar to how we deal with notifications above in SetAuth() |
+ if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { |
+ NotifyAuthCancelled(); |
+ } else { |
+ BrowserThread::PostTask( |
+ BrowserThread::UI, FROM_HERE, |
+ NewRunnableMethod(this, &LoginHandler::NotifyAuthCancelled)); |
+ } |
+ |
BrowserThread::PostTask( |
BrowserThread::UI, FROM_HERE, |
NewRunnableMethod(this, &LoginHandler::CloseContentsDeferred)); |
BrowserThread::PostTask( |
- BrowserThread::UI, FROM_HERE, |
- NewRunnableMethod(this, &LoginHandler::NotifyAuthCancelled)); |
- BrowserThread::PostTask( |
BrowserThread::IO, FROM_HERE, |
NewRunnableMethod(this, &LoginHandler::CancelAuthDeferred)); |
} |
@@ -196,17 +208,17 @@ void LoginHandler::Observe(NotificationType type, |
if (!requesting_contents) |
return; |
- NavigationController* this_controller = &requesting_contents->controller(); |
- NavigationController* that_controller = |
- Source<NavigationController>(source).ptr(); |
- |
- // Only handle notifications from other handlers. |
- if (this_controller == that_controller) |
+ // Break out early if we aren't interested in the notification. |
+ if (WasAuthHandled()) |
return; |
LoginNotificationDetails* login_details = |
Details<LoginNotificationDetails>(details).ptr(); |
+ // WasAuthHandled() should always test positive before we publish |
+ // AUTH_SUPPLIED or AUTH_CANCELLED notifications. |
+ DCHECK(login_details->handler() != this); |
+ |
// Only handle notification for the identical auth info. |
if (*login_details->handler()->auth_info() != *auth_info()) |
return; |
@@ -236,7 +248,7 @@ void LoginHandler::SetDialog(ConstrainedWindow* dialog) { |
void LoginHandler::NotifyAuthNeeded() { |
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
- if (WasAuthHandled(false)) |
+ if (WasAuthHandled()) |
return; |
TabContents* requesting_contents = GetTabContentsForLogin(); |
@@ -254,7 +266,7 @@ void LoginHandler::NotifyAuthNeeded() { |
void LoginHandler::NotifyAuthCancelled() { |
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
- DCHECK(WasAuthHandled(false)); |
+ DCHECK(WasAuthHandled()); |
TabContents* requesting_contents = GetTabContentsForLogin(); |
if (!requesting_contents) |
@@ -272,7 +284,7 @@ void LoginHandler::NotifyAuthCancelled() { |
void LoginHandler::NotifyAuthSupplied(const std::wstring& username, |
const std::wstring& password) { |
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
- DCHECK(WasAuthHandled(false)); |
+ DCHECK(WasAuthHandled()); |
TabContents* requesting_contents = GetTabContentsForLogin(); |
if (!requesting_contents) |
@@ -288,7 +300,7 @@ void LoginHandler::NotifyAuthSupplied(const std::wstring& username, |
} |
void LoginHandler::ReleaseSoon() { |
- if (!WasAuthHandled(true)) { |
+ if (!TestAndSetAuthHandled()) { |
BrowserThread::PostTask( |
BrowserThread::IO, FROM_HERE, |
NewRunnableMethod(this, &LoginHandler::CancelAuthDeferred)); |
@@ -306,12 +318,17 @@ void LoginHandler::ReleaseSoon() { |
} |
// Returns whether authentication had been handled (SetAuth or CancelAuth). |
-// If |set_handled| is true, it will mark authentication as handled. |
-bool LoginHandler::WasAuthHandled(bool set_handled) { |
+bool LoginHandler::WasAuthHandled() const { |
+ AutoLock lock(handled_auth_lock_); |
+ bool was_handled = handled_auth_; |
+ return was_handled; |
+} |
+ |
+// Marks authentication as handled and returns the previous handled state. |
+bool LoginHandler::TestAndSetAuthHandled() { |
AutoLock lock(handled_auth_lock_); |
bool was_handled = handled_auth_; |
- if (set_handled) |
- handled_auth_ = true; |
+ handled_auth_ = true; |
return was_handled; |
} |
@@ -365,7 +382,7 @@ class LoginDialogTask : public Task { |
void Run() { |
TabContents* parent_contents = handler_->GetTabContentsForLogin(); |
- if (!parent_contents) { |
+ if (!parent_contents || handler_->WasAuthHandled()) { |
// The request may have been cancelled, or it may be for a renderer |
// not hosted by a tab (e.g. an extension). Cancel just in case |
// (cancelling twice is a no-op). |