summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortonyg@chromium.org <tonyg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-01 19:41:13 +0000
committertonyg@chromium.org <tonyg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-01 19:41:13 +0000
commit391681a8132c34092e58e7d6152364d76deb8ac3 (patch)
tree9991372cb494df16797d3177d1dc2e1e2348217f
parent80f1f90bad2ed924e25e42bbaaf175b2429f7fe9 (diff)
downloadchromium_src-391681a8132c34092e58e7d6152364d76deb8ac3.zip
chromium_src-391681a8132c34092e58e7d6152364d76deb8ac3.tar.gz
chromium_src-391681a8132c34092e58e7d6152364d76deb8ac3.tar.bz2
Notify all active login prompts when one login prompt is submitted. This allows
identical login prompts to dismiss themselves. This was originally submitted as r42770 but had to be rolled back because I copied from a FLAKY test. Now that r43266 fixed the flakiness, this should be good to go. The code is exactly the same as r42770 with one exception. I added a WaitForNavigation guard to the tab the other tab in the new test cases. This could have also caused flakiness in the initial revision. BUG=8914 TEST=ui_tests --gtest_filter=LoginPromptTest.* Review URL: http://codereview.chromium.org/1528012 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@43374 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/automation/automation_provider_observers.cc4
-rw-r--r--chrome/browser/login_prompt.cc147
-rw-r--r--chrome/browser/login_prompt.h71
-rw-r--r--chrome/browser/login_prompt_gtk.cc10
-rw-r--r--chrome/browser/login_prompt_mac.mm11
-rw-r--r--chrome/browser/login_prompt_uitest.cc70
-rw-r--r--chrome/browser/login_prompt_win.cc10
-rw-r--r--chrome/common/notification_type.h11
-rw-r--r--net/base/auth.h11
9 files changed, 299 insertions, 46 deletions
diff --git a/chrome/browser/automation/automation_provider_observers.cc b/chrome/browser/automation/automation_provider_observers.cc
index 7f1f4f5..7e5ac24 100644
--- a/chrome/browser/automation/automation_provider_observers.cc
+++ b/chrome/browser/automation/automation_provider_observers.cc
@@ -137,6 +137,7 @@ NavigationNotificationObserver::NavigationNotificationObserver(
registrar_.Add(this, NotificationType::LOAD_STOP, source);
registrar_.Add(this, NotificationType::AUTH_NEEDED, source);
registrar_.Add(this, NotificationType::AUTH_SUPPLIED, source);
+ registrar_.Add(this, NotificationType::AUTH_CANCELLED, source);
if (include_current_navigation && controller->tab_contents()->is_loading())
navigation_started_ = true;
@@ -175,7 +176,8 @@ void NavigationNotificationObserver::Observe(
if (--navigations_remaining_ == 0)
ConditionMet(AUTOMATION_MSG_NAVIGATION_SUCCESS);
}
- } else if (type == NotificationType::AUTH_SUPPLIED) {
+ } else if (type == NotificationType::AUTH_SUPPLIED ||
+ type == NotificationType::AUTH_CANCELLED) {
// The LoginHandler for this tab is no longer valid.
automation_->RemoveLoginHandler(controller_);
diff --git a/chrome/browser/login_prompt.cc b/chrome/browser/login_prompt.cc
index 838e16d..b568785 100644
--- a/chrome/browser/login_prompt.cc
+++ b/chrome/browser/login_prompt.cc
@@ -67,18 +67,26 @@ std::string GetSignonRealm(const GURL& url,
// ----------------------------------------------------------------------------
// LoginHandler
-LoginHandler::LoginHandler(URLRequest* request)
+LoginHandler::LoginHandler(net::AuthChallengeInfo* auth_info,
+ URLRequest* request)
: handled_auth_(false),
dialog_(NULL),
+ auth_info_(auth_info),
request_(request),
password_manager_(NULL),
login_model_(NULL) {
// This constructor is called on the I/O thread, so we cannot load the nib
// here. BuildViewForPasswordManager() will be invoked on the UI thread
// later, so wait with loading the nib until then.
- DCHECK(request_) << "LoginHandlerMac constructed with NULL request";
+ DCHECK(request_) << "LoginHandler constructed with NULL request";
+ DCHECK(auth_info_) << "LoginHandler constructed with NULL auth info";
AddRef(); // matched by ReleaseSoon::ReleaseSoon().
+
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(this, &LoginHandler::AddObservers));
+
if (!ResourceDispatcherHost::RenderViewForRequest(
request_, &render_process_host_id_, &tab_contents_id_)) {
NOTREACHED();
@@ -121,11 +129,12 @@ void LoginHandler::SetAuth(const std::wstring& username,
NewRunnableMethod(this, &LoginHandler::CloseContentsDeferred));
ChromeThread::PostTask(
ChromeThread::UI, FROM_HERE,
- NewRunnableMethod(this, &LoginHandler::SendNotifications));
+ NewRunnableMethod(
+ this, &LoginHandler::NotifyAuthSupplied, username, password));
ChromeThread::PostTask(
ChromeThread::IO, FROM_HERE,
- NewRunnableMethod(this, &LoginHandler::SetAuthDeferred,
- username, password));
+ NewRunnableMethod(
+ this, &LoginHandler::SetAuthDeferred, username, password));
}
void LoginHandler::CancelAuth() {
@@ -137,7 +146,7 @@ void LoginHandler::CancelAuth() {
NewRunnableMethod(this, &LoginHandler::CloseContentsDeferred));
ChromeThread::PostTask(
ChromeThread::UI, FROM_HERE,
- NewRunnableMethod(this, &LoginHandler::SendNotifications));
+ NewRunnableMethod(this, &LoginHandler::NotifyAuthCancelled));
ChromeThread::PostTask(
ChromeThread::IO, FROM_HERE,
NewRunnableMethod(this, &LoginHandler::CancelAuthDeferred));
@@ -154,6 +163,63 @@ void LoginHandler::OnRequestCancelled() {
CancelAuth();
}
+void LoginHandler::AddObservers() {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+
+ registrar_.Add(this, NotificationType::AUTH_SUPPLIED,
+ NotificationService::AllSources());
+ registrar_.Add(this, NotificationType::AUTH_CANCELLED,
+ NotificationService::AllSources());
+}
+
+void LoginHandler::RemoveObservers() {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+
+ registrar_.Remove(this, NotificationType::AUTH_SUPPLIED,
+ NotificationService::AllSources());
+ registrar_.Remove(this, NotificationType::AUTH_CANCELLED,
+ NotificationService::AllSources());
+
+ DCHECK(registrar_.IsEmpty());
+}
+
+void LoginHandler::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ DCHECK(type == NotificationType::AUTH_SUPPLIED ||
+ type == NotificationType::AUTH_CANCELLED);
+
+ TabContents* requesting_contents = GetTabContentsForLogin();
+ 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)
+ return;
+
+ LoginNotificationDetails* login_details =
+ Details<LoginNotificationDetails>(details).ptr();
+
+ // Only handle notification for the identical auth info.
+ if (*login_details->handler()->auth_info() != *auth_info())
+ return;
+
+ // Set or cancel the auth in this handler.
+ if (type == NotificationType::AUTH_SUPPLIED) {
+ AuthSuppliedLoginNotificationDetails* supplied_details =
+ Details<AuthSuppliedLoginNotificationDetails>(details).ptr();
+ SetAuth(supplied_details->username(), supplied_details->password());
+ } else {
+ DCHECK(type == NotificationType::AUTH_CANCELLED);
+ CancelAuth();
+ }
+}
+
void LoginHandler::SetModel(LoginModel* model) {
if (login_model_)
login_model_->SetObserver(NULL);
@@ -166,28 +232,57 @@ void LoginHandler::SetDialog(ConstrainedWindow* dialog) {
dialog_ = dialog;
}
-// Notify observers that authentication is needed or received. The automation
-// proxy uses this for testing.
-void LoginHandler::SendNotifications() {
+void LoginHandler::NotifyAuthNeeded() {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ if (WasAuthHandled(false))
+ return;
+
+ TabContents* requesting_contents = GetTabContentsForLogin();
+ if (!requesting_contents)
+ return;
NotificationService* service = NotificationService::current();
+ NavigationController* controller = &requesting_contents->controller();
+ LoginNotificationDetails details(this);
+
+ service->Notify(NotificationType::AUTH_NEEDED,
+ Source<NavigationController>(controller),
+ Details<LoginNotificationDetails>(&details));
+}
+
+void LoginHandler::NotifyAuthCancelled() {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ DCHECK(WasAuthHandled(false));
+
TabContents* requesting_contents = GetTabContentsForLogin();
if (!requesting_contents)
return;
+ NotificationService* service = NotificationService::current();
NavigationController* controller = &requesting_contents->controller();
+ LoginNotificationDetails details(this);
- if (!WasAuthHandled(false)) {
- LoginNotificationDetails details(this);
- service->Notify(NotificationType::AUTH_NEEDED,
- Source<NavigationController>(controller),
- Details<LoginNotificationDetails>(&details));
- } else {
- service->Notify(NotificationType::AUTH_SUPPLIED,
- Source<NavigationController>(controller),
- NotificationService::NoDetails());
- }
+ service->Notify(NotificationType::AUTH_CANCELLED,
+ Source<NavigationController>(controller),
+ Details<LoginNotificationDetails>(&details));
+}
+
+void LoginHandler::NotifyAuthSupplied(const std::wstring& username,
+ const std::wstring& password) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ DCHECK(WasAuthHandled(false));
+
+ TabContents* requesting_contents = GetTabContentsForLogin();
+ if (!requesting_contents)
+ return;
+
+ NotificationService* service = NotificationService::current();
+ NavigationController* controller = &requesting_contents->controller();
+ AuthSuppliedLoginNotificationDetails details(this, username, password);
+
+ service->Notify(NotificationType::AUTH_SUPPLIED,
+ Source<NavigationController>(controller),
+ Details<AuthSuppliedLoginNotificationDetails>(&details));
}
void LoginHandler::ReleaseSoon() {
@@ -197,9 +292,13 @@ void LoginHandler::ReleaseSoon() {
NewRunnableMethod(this, &LoginHandler::CancelAuthDeferred));
ChromeThread::PostTask(
ChromeThread::UI, FROM_HERE,
- NewRunnableMethod(this, &LoginHandler::SendNotifications));
+ NewRunnableMethod(this, &LoginHandler::NotifyAuthCancelled));
}
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(this, &LoginHandler::RemoveObservers));
+
// Delete this object once all InvokeLaters have been called.
ChromeThread::ReleaseSoon(ChromeThread::IO, FROM_HERE, this);
}
@@ -231,7 +330,7 @@ void LoginHandler::CancelAuthDeferred() {
if (request_) {
request_->CancelAuth();
- // Verify that CancelAuth does destroy the request via our delegate.
+ // Verify that CancelAuth doesn't destroy the request via our delegate.
DCHECK(request_ != NULL);
ResetLoginHandlerForRequest(request_);
}
@@ -303,7 +402,7 @@ class LoginDialogTask : public Task {
std::string host_and_port(WideToASCII(auth_info_->host_and_port));
if (net::GetHostAndPort(request_url_) != host_and_port) {
dialog_form.origin = GURL();
- NOTREACHED();
+ NOTREACHED(); // crbug.com/32718
} else if (auth_info_->is_proxy) {
std::string origin = host_and_port;
// We don't expect this to already start with http:// or https://.
@@ -329,7 +428,7 @@ class LoginDialogTask : public Task {
// This is owned by the ResourceDispatcherHost that invoked us.
LoginHandler* handler_;
- DISALLOW_EVIL_CONSTRUCTORS(LoginDialogTask);
+ DISALLOW_COPY_AND_ASSIGN(LoginDialogTask);
};
// ----------------------------------------------------------------------------
@@ -337,7 +436,7 @@ class LoginDialogTask : public Task {
LoginHandler* CreateLoginPrompt(net::AuthChallengeInfo* auth_info,
URLRequest* request) {
- LoginHandler* handler = LoginHandler::Create(request);
+ LoginHandler* handler = LoginHandler::Create(auth_info, request);
ChromeThread::PostTask(
ChromeThread::UI, FROM_HERE, new LoginDialogTask(
request->url(), auth_info, handler));
diff --git a/chrome/browser/login_prompt.h b/chrome/browser/login_prompt.h
index 7314dbf..946b27f 100644
--- a/chrome/browser/login_prompt.h
+++ b/chrome/browser/login_prompt.h
@@ -9,7 +9,10 @@
#include "base/basictypes.h"
#include "base/lock.h"
+#include "base/ref_counted.h"
#include "chrome/browser/password_manager/password_manager.h"
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_registrar.h"
namespace net {
class AuthChallengeInfo;
@@ -24,14 +27,16 @@ class URLRequest;
// authentication info to the URLRequest that needs it. These functions must
// be implemented in a thread safe manner.
class LoginHandler : public base::RefCountedThreadSafe<LoginHandler>,
- public LoginModelObserver {
+ public LoginModelObserver,
+ public NotificationObserver {
public:
- explicit LoginHandler(URLRequest* request);
+ LoginHandler(net::AuthChallengeInfo* auth_info, URLRequest* request);
virtual ~LoginHandler();
// Builds the platform specific LoginHandler. Used from within
// CreateLoginPrompt() which creates tasks.
- static LoginHandler* Create(URLRequest* request);
+ static LoginHandler* Create(net::AuthChallengeInfo* auth_info,
+ URLRequest* request);
// Initializes the underlying platform specific view.
virtual void BuildViewForPasswordManager(PasswordManager* manager,
@@ -57,19 +62,41 @@ class LoginHandler : public base::RefCountedThreadSafe<LoginHandler>,
// This function can only be called from the IO thread.
void OnRequestCancelled();
- protected:
+ // Implements the NotificationObserver interface.
+ // Listens for AUTH_SUPPLIED and AUTH_CANCELLED notifications from other
+ // LoginHandlers so that this LoginHandler has the chance to dismiss itself
+ // if it was waiting for the same authentication.
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+ protected:
void SetModel(LoginModel* model);
void SetDialog(ConstrainedWindow* dialog);
- // Notify observers that authentication is needed or received. The automation
- // proxy uses this for testing.
- void SendNotifications();
+ // Notify observers that authentication is needed.
+ void NotifyAuthNeeded();
+ // Performs necessary cleanup before deletion.
void ReleaseSoon();
+ // Who/where/what asked for the authentication.
+ net::AuthChallengeInfo* auth_info() const { return auth_info_.get(); }
+
private:
+ // Starts observing notifications from other LoginHandlers.
+ void AddObservers();
+
+ // Stops observing notifications from other LoginHandlers.
+ void RemoveObservers();
+
+ // Notify observers that authentication is supplied.
+ void NotifyAuthSupplied(const std::wstring& username,
+ const std::wstring& password);
+
+ // Notify observers that authentication is cancelled.
+ void NotifyAuthCancelled();
// Returns whether authentication had been handled (SetAuth or CancelAuth).
// If |set_handled| is true, it will mark authentication as handled.
@@ -93,6 +120,9 @@ class LoginHandler : public base::RefCountedThreadSafe<LoginHandler>,
// This should only be accessed on the UI loop.
ConstrainedWindow* dialog_;
+ // Who/where/what asked for the authentication.
+ scoped_refptr<net::AuthChallengeInfo> auth_info_;
+
// The request that wants login data.
// This should only be accessed on the IO loop.
URLRequest* request_;
@@ -115,6 +145,9 @@ class LoginHandler : public base::RefCountedThreadSafe<LoginHandler>,
// If not null, points to a model we need to notify of our own destruction
// so it doesn't try and access this when its too late.
LoginModel* login_model_;
+
+ // Observes other login handlers so this login handler can respond.
+ NotificationRegistrar registrar_;
};
// Details to provide the NotificationObserver. Used by the automation proxy
@@ -133,6 +166,30 @@ class LoginNotificationDetails {
DISALLOW_COPY_AND_ASSIGN(LoginNotificationDetails);
};
+// Details to provide the NotificationObserver. Used by the automation proxy
+// for testing and by other LoginHandlers to dismiss themselves when an
+// identical auth is supplied.
+class AuthSuppliedLoginNotificationDetails : public LoginNotificationDetails {
+ public:
+ AuthSuppliedLoginNotificationDetails(LoginHandler* handler,
+ const std::wstring& username,
+ const std::wstring& password)
+ : LoginNotificationDetails(handler),
+ username_(username),
+ password_(password) {}
+ const std::wstring& username() const { return username_; }
+ const std::wstring& password() const { return password_; }
+
+ private:
+ // The username that was used for the authentication.
+ const std::wstring username_;
+
+ // The password that was used for the authentication.
+ const std::wstring password_;
+
+ DISALLOW_COPY_AND_ASSIGN(AuthSuppliedLoginNotificationDetails);
+};
+
// Prompts the user for their username and password. This is designed to
// be called on the background (I/O) thread, in response to
// URLRequest::Delegate::OnAuthRequired. The prompt will be created
diff --git a/chrome/browser/login_prompt_gtk.cc b/chrome/browser/login_prompt_gtk.cc
index 92ba1dd..24d6084 100644
--- a/chrome/browser/login_prompt_gtk.cc
+++ b/chrome/browser/login_prompt_gtk.cc
@@ -33,7 +33,8 @@ using webkit_glue::PasswordForm;
class LoginHandlerGtk : public LoginHandler,
public ConstrainedWindowGtkDelegate {
public:
- explicit LoginHandlerGtk(URLRequest* request) : LoginHandler(request) {
+ LoginHandlerGtk(net::AuthChallengeInfo* auth_info, URLRequest* request)
+ : LoginHandler(auth_info, request) {
}
virtual ~LoginHandlerGtk() {
@@ -107,7 +108,7 @@ class LoginHandlerGtk : public LoginHandler,
// to happen after this is called (since this was InvokeLater'd first).
SetDialog(GetTabContentsForLogin()->CreateConstrainedDialog(this));
- SendNotifications();
+ NotifyAuthNeeded();
}
// Overridden from ConstrainedWindowGtkDelegate:
@@ -170,6 +171,7 @@ class LoginHandlerGtk : public LoginHandler,
};
// static
-LoginHandler* LoginHandler::Create(URLRequest* request) {
- return new LoginHandlerGtk(request);
+LoginHandler* LoginHandler::Create(net::AuthChallengeInfo* auth_info,
+ URLRequest* request) {
+ return new LoginHandlerGtk(auth_info, request);
}
diff --git a/chrome/browser/login_prompt_mac.mm b/chrome/browser/login_prompt_mac.mm
index e39faa3..a211af6 100644
--- a/chrome/browser/login_prompt_mac.mm
+++ b/chrome/browser/login_prompt_mac.mm
@@ -35,8 +35,8 @@ using webkit_glue::PasswordForm;
class LoginHandlerMac : public LoginHandler,
public ConstrainedWindowMacDelegateCustomSheet {
public:
- explicit LoginHandlerMac(URLRequest* request)
- : LoginHandler(request),
+ LoginHandlerMac(net::AuthChallengeInfo* auth_info, URLRequest* request)
+ : LoginHandler(auth_info, request),
sheet_controller_(nil) {
}
@@ -74,7 +74,7 @@ class LoginHandlerMac : public LoginHandler,
// to happen after this is called (since this was InvokeLater'd first).
SetDialog(GetTabContentsForLogin()->CreateConstrainedDialog(this));
- SendNotifications();
+ NotifyAuthNeeded();
}
// Overridden from ConstrainedWindowMacDelegate:
@@ -116,8 +116,9 @@ class LoginHandlerMac : public LoginHandler,
};
// static
-LoginHandler* LoginHandler::Create(URLRequest* request) {
- return new LoginHandlerMac(request);
+LoginHandler* LoginHandler::Create(net::AuthChallengeInfo* auth_info,
+ URLRequest* request) {
+ return new LoginHandlerMac(auth_info, request);
}
// ----------------------------------------------------------------------------
diff --git a/chrome/browser/login_prompt_uitest.cc b/chrome/browser/login_prompt_uitest.cc
index 63f2282..c260181 100644
--- a/chrome/browser/login_prompt_uitest.cc
+++ b/chrome/browser/login_prompt_uitest.cc
@@ -165,3 +165,73 @@ TEST_F(LoginPromptTest, TestCancelAuth) {
EXPECT_FALSE(tab->NeedsAuth());
EXPECT_EQ(L"Denied: no auth", GetActiveTabTitle());
}
+
+// If multiple tabs are looking for the same auth, the user should only have to
+// enter it once (http://crbug.com/8914).
+TEST_F(LoginPromptTest, SupplyRedundantAuths) {
+ scoped_refptr<HTTPTestServer> server =
+ HTTPTestServer::CreateServer(kDocRoot, NULL);
+ ASSERT_TRUE(NULL != server.get());
+
+ scoped_refptr<TabProxy> basic_tab1(GetActiveTab());
+ ASSERT_TRUE(basic_tab1.get());
+ ASSERT_TRUE(
+ basic_tab1->NavigateToURL(server->TestServerPageW(L"auth-basic/1")));
+ EXPECT_TRUE(basic_tab1->NeedsAuth());
+
+ AppendTab(GURL(chrome::kAboutBlankURL));
+ scoped_refptr<TabProxy> basic_tab2(GetActiveTab());
+ ASSERT_TRUE(basic_tab2.get());
+ ASSERT_TRUE(
+ basic_tab2->NavigateToURL(server->TestServerPageW(L"auth-basic/2")));
+ EXPECT_TRUE(basic_tab2->NeedsAuth());
+
+ // Set the auth in only one of the tabs (but wait for the other to load).
+ int64 last_navigation_time;
+ ASSERT_TRUE(basic_tab2->GetLastNavigationTime(&last_navigation_time));
+ EXPECT_TRUE(basic_tab1->SetAuth(username_basic_, password_));
+ EXPECT_TRUE(basic_tab2->WaitForNavigation(last_navigation_time));
+
+ // Now both tabs have loaded.
+ wstring title1;
+ EXPECT_TRUE(basic_tab1->GetTabTitle(&title1));
+ EXPECT_EQ(ExpectedTitleFromAuth(username_basic_, password_), title1);
+ wstring title2;
+ EXPECT_TRUE(basic_tab2->GetTabTitle(&title2));
+ EXPECT_EQ(ExpectedTitleFromAuth(username_basic_, password_), title2);
+}
+
+// If multiple tabs are looking for the same auth, and one is cancelled, the
+// other should be cancelled as well.
+TEST_F(LoginPromptTest, CancelRedundantAuths) {
+ scoped_refptr<HTTPTestServer> server =
+ HTTPTestServer::CreateServer(kDocRoot, NULL);
+ ASSERT_TRUE(NULL != server.get());
+
+ scoped_refptr<TabProxy> basic_tab1(GetActiveTab());
+ ASSERT_TRUE(basic_tab1.get());
+ ASSERT_TRUE(
+ basic_tab1->NavigateToURL(server->TestServerPageW(L"auth-basic/1")));
+ EXPECT_TRUE(basic_tab1->NeedsAuth());
+
+ AppendTab(GURL(chrome::kAboutBlankURL));
+ scoped_refptr<TabProxy> basic_tab2(GetActiveTab());
+ ASSERT_TRUE(basic_tab2.get());
+ ASSERT_TRUE(
+ basic_tab2->NavigateToURL(server->TestServerPageW(L"auth-basic/2")));
+ EXPECT_TRUE(basic_tab2->NeedsAuth());
+
+ // Cancel the auth in only one of the tabs (but wait for the other to load).
+ int64 last_navigation_time;
+ ASSERT_TRUE(basic_tab2->GetLastNavigationTime(&last_navigation_time));
+ EXPECT_TRUE(basic_tab1->CancelAuth());
+ EXPECT_TRUE(basic_tab2->WaitForNavigation(last_navigation_time));
+
+ // Now both tabs have been denied.
+ wstring title1;
+ EXPECT_TRUE(basic_tab1->GetTabTitle(&title1));
+ EXPECT_EQ(L"Denied: no auth", title1);
+ wstring title2;
+ EXPECT_TRUE(basic_tab2->GetTabTitle(&title2));
+ EXPECT_EQ(L"Denied: no auth", title2);
+}
diff --git a/chrome/browser/login_prompt_win.cc b/chrome/browser/login_prompt_win.cc
index 13e1664..39f1497 100644
--- a/chrome/browser/login_prompt_win.cc
+++ b/chrome/browser/login_prompt_win.cc
@@ -31,7 +31,8 @@ using webkit_glue::PasswordForm;
class LoginHandlerWin : public LoginHandler,
public ConstrainedDialogDelegate {
public:
- explicit LoginHandlerWin(URLRequest* request) : LoginHandler(request) {
+ LoginHandlerWin(net::AuthChallengeInfo* auth_info, URLRequest* request)
+ : LoginHandler(auth_info, request) {
}
// LoginModelObserver implementation.
@@ -119,7 +120,7 @@ class LoginHandlerWin : public LoginHandler,
// will occur via an InvokeLater on the UI thread, which is guaranteed
// to happen after this is called (since this was InvokeLater'd first).
SetDialog(GetTabContentsForLogin()->CreateConstrainedDialog(this));
- SendNotifications();
+ NotifyAuthNeeded();
}
private:
@@ -135,6 +136,7 @@ class LoginHandlerWin : public LoginHandler,
};
// static
-LoginHandler* LoginHandler::Create(URLRequest* request) {
- return new LoginHandlerWin(request);
+LoginHandler* LoginHandler::Create(net::AuthChallengeInfo* auth_info,
+ URLRequest* request) {
+ return new LoginHandlerWin(auth_info, request);
}
diff --git a/chrome/common/notification_type.h b/chrome/common/notification_type.h
index 2e437d7..d00a33d 100644
--- a/chrome/common/notification_type.h
+++ b/chrome/common/notification_type.h
@@ -508,9 +508,18 @@ class NotificationType {
// by the user or by an automation service), but before we've actually
// received another response from the server. The source is the
// Source<NavigationController> for the tab in which the prompt was shown.
- // No details are expected.
+ // Details are an AuthSuppliedLoginNotificationDetails which provide the
+ // LoginHandler that should be given authentication as well as the supplied
+ // username and password.
AUTH_SUPPLIED,
+ // This is sent when an authentication request has been dismissed without
+ // supplying credentials (either by the user or by an automation service).
+ // The source is the Source<NavigationController> for the tab in which the
+ // prompt was shown. Details are a LoginNotificationDetails which provide
+ // the LoginHandler that should be cancelled.
+ AUTH_CANCELLED,
+
// Saved Pages -------------------------------------------------------------
// Sent when a SavePackage finishes successfully. The source is the
diff --git a/net/base/auth.h b/net/base/auth.h
index ec31bfc..1704e5b 100644
--- a/net/base/auth.h
+++ b/net/base/auth.h
@@ -16,6 +16,17 @@ namespace net {
class AuthChallengeInfo :
public base::RefCountedThreadSafe<AuthChallengeInfo> {
public:
+ bool operator==(const AuthChallengeInfo& that) const {
+ return (this->is_proxy == that.is_proxy &&
+ this->host_and_port == that.host_and_port &&
+ this->scheme == that.scheme &&
+ this->realm == that.realm);
+ }
+
+ bool operator!=(const AuthChallengeInfo& that) const {
+ return !(*this == that);
+ }
+
bool is_proxy; // true for Proxy-Authenticate, false for WWW-Authenticate.
std::wstring host_and_port; // <host>:<port> of the server asking for auth
// (could be the proxy).