summaryrefslogtreecommitdiffstats
path: root/chrome/browser/ui/login
diff options
context:
space:
mode:
authorben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-12-02 21:18:33 +0000
committerben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-12-02 21:18:33 +0000
commit8ecad5ed4a616e8c5023561c224e81e09a37d79a (patch)
tree86f596a20c73edf5a39eb0a4e589fe654c15ba2c /chrome/browser/ui/login
parent96ccc7ec1f107b74a697ced6d08c4150efb5c297 (diff)
downloadchromium_src-8ecad5ed4a616e8c5023561c224e81e09a37d79a.zip
chromium_src-8ecad5ed4a616e8c5023561c224e81e09a37d79a.tar.gz
chromium_src-8ecad5ed4a616e8c5023561c224e81e09a37d79a.tar.bz2
Move:
file_path_watcher into subdir profile* into profiles/ subdir login* into ui/login visitedlink* into subdir BUG=none TEST=none TBR=brettw Review URL: http://codereview.chromium.org/5606002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@68069 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/ui/login')
-rw-r--r--chrome/browser/ui/login/login_model.h35
-rw-r--r--chrome/browser/ui/login/login_prompt.cc453
-rw-r--r--chrome/browser/ui/login/login_prompt.h214
-rw-r--r--chrome/browser/ui/login/login_prompt_gtk.cc199
-rw-r--r--chrome/browser/ui/login/login_prompt_mac.h34
-rw-r--r--chrome/browser/ui/login/login_prompt_mac.mm191
-rw-r--r--chrome/browser/ui/login/login_prompt_uitest.cc280
-rw-r--r--chrome/browser/ui/login/login_prompt_unittest.cc42
-rw-r--r--chrome/browser/ui/login/login_prompt_win.cc147
9 files changed, 1595 insertions, 0 deletions
diff --git a/chrome/browser/ui/login/login_model.h b/chrome/browser/ui/login/login_model.h
new file mode 100644
index 0000000..94d3478
--- /dev/null
+++ b/chrome/browser/ui/login/login_model.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_LOGIN_LOGIN_MODEL_H_
+#define CHROME_BROWSER_UI_LOGIN_LOGIN_MODEL_H_
+#pragma once
+
+#include <string>
+
+// Simple Model & Observer interfaces for a LoginView to facilitate exchanging
+// information.
+class LoginModelObserver {
+ public:
+ // Called by the model when a username,password pair has been identified
+ // as a match for the pending login prompt.
+ virtual void OnAutofillDataAvailable(const std::wstring& username,
+ const std::wstring& password) = 0;
+
+ protected:
+ virtual ~LoginModelObserver() {}
+};
+
+class LoginModel {
+ public:
+ // Set the observer interested in the data from the model.
+ // observer can be null, signifying there is no longer any observer
+ // interested in the data.
+ virtual void SetObserver(LoginModelObserver* observer) = 0;
+
+ protected:
+ virtual ~LoginModel() {}
+};
+
+#endif // CHROME_BROWSER_UI_LOGIN_LOGIN_MODEL_H_
diff --git a/chrome/browser/ui/login/login_prompt.cc b/chrome/browser/ui/login/login_prompt.cc
new file mode 100644
index 0000000..d653457
--- /dev/null
+++ b/chrome/browser/ui/login/login_prompt.cc
@@ -0,0 +1,453 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/login/login_prompt.h"
+
+#include <vector>
+
+#include "app/l10n_util.h"
+#include "base/command_line.h"
+#include "base/lock.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/browser_thread.h"
+#include "chrome/browser/password_manager/password_manager.h"
+#include "chrome/browser/renderer_host/render_process_host.h"
+#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
+#include "chrome/browser/renderer_host/resource_dispatcher_host_request_info.h"
+#include "chrome/browser/tab_contents/constrained_window.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/tab_contents/tab_util.h"
+#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/notification_service.h"
+#include "grit/generated_resources.h"
+#include "net/base/auth.h"
+#include "net/base/net_util.h"
+#include "net/url_request/url_request.h"
+
+using webkit_glue::PasswordForm;
+
+class LoginHandlerImpl;
+
+// Helper to remove the ref from an net::URLRequest to the LoginHandler.
+// Should only be called from the IO thread, since it accesses an
+// net::URLRequest.
+void ResetLoginHandlerForRequest(net::URLRequest* request) {
+ ResourceDispatcherHostRequestInfo* info =
+ ResourceDispatcherHost::InfoForRequest(request);
+ if (!info)
+ return;
+
+ info->set_login_handler(NULL);
+}
+
+// Get the signon_realm under which this auth info should be stored.
+//
+// The format of the signon_realm for proxy auth is:
+// proxy-host/auth-realm
+// The format of the signon_realm for server auth is:
+// url-scheme://url-host[:url-port]/auth-realm
+//
+// Be careful when changing this function, since you could make existing
+// saved logins un-retrievable.
+std::string GetSignonRealm(const GURL& url,
+ const net::AuthChallengeInfo& auth_info) {
+ std::string signon_realm;
+ if (auth_info.is_proxy) {
+ signon_realm = WideToASCII(auth_info.host_and_port);
+ signon_realm.append("/");
+ } else {
+ // Take scheme, host, and port from the url.
+ signon_realm = url.GetOrigin().spec();
+ // This ends with a "/".
+ }
+ signon_realm.append(WideToUTF8(auth_info.realm));
+ return signon_realm;
+}
+
+// ----------------------------------------------------------------------------
+// LoginHandler
+
+LoginHandler::LoginHandler(net::AuthChallengeInfo* auth_info,
+ net::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_) << "LoginHandler constructed with NULL request";
+ DCHECK(auth_info_) << "LoginHandler constructed with NULL auth info";
+
+ AddRef(); // matched by LoginHandler::ReleaseSoon().
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableMethod(this, &LoginHandler::AddObservers));
+
+ if (!ResourceDispatcherHost::RenderViewForRequest(
+ request_, &render_process_host_id_, &tab_contents_id_)) {
+ NOTREACHED();
+ }
+}
+
+LoginHandler::~LoginHandler() {
+ SetModel(NULL);
+}
+
+void LoginHandler::SetPasswordForm(const webkit_glue::PasswordForm& form) {
+ password_form_ = form;
+}
+
+void LoginHandler::SetPasswordManager(PasswordManager* password_manager) {
+ password_manager_ = password_manager;
+}
+
+TabContents* LoginHandler::GetTabContentsForLogin() const {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ return tab_util::GetTabContentsByID(render_process_host_id_,
+ tab_contents_id_);
+}
+
+void LoginHandler::SetAuth(const std::wstring& username,
+ const std::wstring& password) {
+ if (WasAuthHandled(true))
+ return;
+
+ // Tell the password manager the credentials were submitted / accepted.
+ if (password_manager_) {
+ password_form_.username_value = WideToUTF16Hack(username);
+ password_form_.password_value = WideToUTF16Hack(password);
+ password_manager_->ProvisionallySavePassword(password_form_);
+ }
+
+ 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))
+ return;
+
+ 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));
+}
+
+void LoginHandler::OnRequestCancelled() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)) <<
+ "Why is OnRequestCancelled called from the UI thread?";
+
+ // Reference is no longer valid.
+ request_ = NULL;
+
+ // Give up on auth if the request was cancelled.
+ CancelAuth();
+}
+
+void LoginHandler::AddObservers() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ registrar_.Add(this, NotificationType::AUTH_SUPPLIED,
+ NotificationService::AllSources());
+ registrar_.Add(this, NotificationType::AUTH_CANCELLED,
+ NotificationService::AllSources());
+}
+
+void LoginHandler::RemoveObservers() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::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(BrowserThread::CurrentlyOn(BrowserThread::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);
+ login_model_ = model;
+ if (login_model_)
+ login_model_->SetObserver(this);
+}
+
+void LoginHandler::SetDialog(ConstrainedWindow* dialog) {
+ dialog_ = dialog;
+}
+
+void LoginHandler::NotifyAuthNeeded() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::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(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(WasAuthHandled(false));
+
+ TabContents* requesting_contents = GetTabContentsForLogin();
+ if (!requesting_contents)
+ return;
+
+ NotificationService* service = NotificationService::current();
+ NavigationController* controller = &requesting_contents->controller();
+ LoginNotificationDetails details(this);
+
+ service->Notify(NotificationType::AUTH_CANCELLED,
+ Source<NavigationController>(controller),
+ Details<LoginNotificationDetails>(&details));
+}
+
+void LoginHandler::NotifyAuthSupplied(const std::wstring& username,
+ const std::wstring& password) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::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() {
+ if (!WasAuthHandled(true)) {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ NewRunnableMethod(this, &LoginHandler::CancelAuthDeferred));
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableMethod(this, &LoginHandler::NotifyAuthCancelled));
+ }
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableMethod(this, &LoginHandler::RemoveObservers));
+
+ // Delete this object once all InvokeLaters have been called.
+ BrowserThread::ReleaseSoon(BrowserThread::IO, FROM_HERE, this);
+}
+
+// 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) {
+ AutoLock lock(handled_auth_lock_);
+ bool was_handled = handled_auth_;
+ if (set_handled)
+ handled_auth_ = true;
+ return was_handled;
+}
+
+// Calls SetAuth from the IO loop.
+void LoginHandler::SetAuthDeferred(const std::wstring& username,
+ const std::wstring& password) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ if (request_) {
+ request_->SetAuth(WideToUTF16Hack(username), WideToUTF16Hack(password));
+ ResetLoginHandlerForRequest(request_);
+ }
+}
+
+// Calls CancelAuth from the IO loop.
+void LoginHandler::CancelAuthDeferred() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ if (request_) {
+ request_->CancelAuth();
+ // Verify that CancelAuth doesn't destroy the request via our delegate.
+ DCHECK(request_ != NULL);
+ ResetLoginHandlerForRequest(request_);
+ }
+}
+
+// Closes the view_contents from the UI loop.
+void LoginHandler::CloseContentsDeferred() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ // The hosting ConstrainedWindow may have been freed.
+ if (dialog_)
+ dialog_->CloseConstrainedWindow();
+}
+
+// ----------------------------------------------------------------------------
+// LoginDialogTask
+
+// This task is run on the UI thread and creates a constrained window with
+// a LoginView to prompt the user. The response will be sent to LoginHandler,
+// which then routes it to the net::URLRequest on the I/O thread.
+class LoginDialogTask : public Task {
+ public:
+ LoginDialogTask(const GURL& request_url,
+ net::AuthChallengeInfo* auth_info,
+ LoginHandler* handler)
+ : request_url_(request_url), auth_info_(auth_info), handler_(handler) {
+ }
+ virtual ~LoginDialogTask() {
+ }
+
+ void Run() {
+ TabContents* parent_contents = handler_->GetTabContentsForLogin();
+ if (!parent_contents) {
+ // 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).
+ handler_->CancelAuth();
+ return;
+ }
+
+ // Tell the password manager to look for saved passwords.
+ TabContentsWrapper** wrapper =
+ TabContentsWrapper::property_accessor()->GetProperty(
+ parent_contents->property_bag());
+ if (!wrapper)
+ return;
+ PasswordManager* password_manager = (*wrapper)->GetPasswordManager();
+ std::vector<PasswordForm> v;
+ MakeInputForPasswordManager(&v);
+ password_manager->PasswordFormsFound(v);
+ handler_->SetPasswordManager(password_manager);
+
+ std::wstring explanation = auth_info_->realm.empty() ?
+ l10n_util::GetStringF(IDS_LOGIN_DIALOG_DESCRIPTION_NO_REALM,
+ auth_info_->host_and_port) :
+ l10n_util::GetStringF(IDS_LOGIN_DIALOG_DESCRIPTION,
+ auth_info_->host_and_port,
+ auth_info_->realm);
+ handler_->BuildViewForPasswordManager(password_manager,
+ explanation);
+ }
+
+ private:
+ // Helper to create a PasswordForm and stuff it into a vector as input
+ // for PasswordManager::PasswordFormsFound, the hook into PasswordManager.
+ void MakeInputForPasswordManager(
+ std::vector<PasswordForm>* password_manager_input) {
+ PasswordForm dialog_form;
+ if (LowerCaseEqualsASCII(auth_info_->scheme, "basic")) {
+ dialog_form.scheme = PasswordForm::SCHEME_BASIC;
+ } else if (LowerCaseEqualsASCII(auth_info_->scheme, "digest")) {
+ dialog_form.scheme = PasswordForm::SCHEME_DIGEST;
+ } else {
+ dialog_form.scheme = PasswordForm::SCHEME_OTHER;
+ }
+ std::string host_and_port(WideToASCII(auth_info_->host_and_port));
+ if (auth_info_->is_proxy) {
+ std::string origin = host_and_port;
+ // We don't expect this to already start with http:// or https://.
+ DCHECK(origin.find("http://") != 0 && origin.find("https://") != 0);
+ origin = std::string("http://") + origin;
+ dialog_form.origin = GURL(origin);
+ } else if (net::GetHostAndPort(request_url_) != host_and_port) {
+ dialog_form.origin = GURL();
+ NOTREACHED(); // crbug.com/32718
+ } else {
+ dialog_form.origin = GURL(request_url_.scheme() + "://" + host_and_port);
+ }
+ dialog_form.signon_realm = GetSignonRealm(dialog_form.origin, *auth_info_);
+ password_manager_input->push_back(dialog_form);
+ // Set the password form for the handler (by copy).
+ handler_->SetPasswordForm(dialog_form);
+ }
+
+ // The url from the net::URLRequest initiating the auth challenge.
+ GURL request_url_;
+
+ // Info about who/where/what is asking for authentication.
+ scoped_refptr<net::AuthChallengeInfo> auth_info_;
+
+ // Where to send the authentication when obtained.
+ // This is owned by the ResourceDispatcherHost that invoked us.
+ LoginHandler* handler_;
+
+ DISALLOW_COPY_AND_ASSIGN(LoginDialogTask);
+};
+
+// ----------------------------------------------------------------------------
+// Public API
+
+LoginHandler* CreateLoginPrompt(net::AuthChallengeInfo* auth_info,
+ net::URLRequest* request) {
+ LoginHandler* handler = LoginHandler::Create(auth_info, request);
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE, new LoginDialogTask(
+ request->url(), auth_info, handler));
+ return handler;
+}
diff --git a/chrome/browser/ui/login/login_prompt.h b/chrome/browser/ui/login/login_prompt.h
new file mode 100644
index 0000000..8e3d15e
--- /dev/null
+++ b/chrome/browser/ui/login/login_prompt.h
@@ -0,0 +1,214 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_LOGIN_LOGIN_PROMPT_H_
+#define CHROME_BROWSER_UI_LOGIN_LOGIN_PROMPT_H_
+#pragma once
+
+#include <string>
+
+#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;
+class URLRequest;
+} // namespace net
+
+class ConstrainedWindow;
+class GURL;
+
+// This is the base implementation for the OS-specific classes that route
+// authentication info to the net::URLRequest that needs it. These functions
+// must be implemented in a thread safe manner.
+class LoginHandler : public base::RefCountedThreadSafe<LoginHandler>,
+ public LoginModelObserver,
+ public NotificationObserver {
+ public:
+ LoginHandler(net::AuthChallengeInfo* auth_info, net::URLRequest* request);
+ virtual ~LoginHandler();
+
+ // Builds the platform specific LoginHandler. Used from within
+ // CreateLoginPrompt() which creates tasks.
+ static LoginHandler* Create(net::AuthChallengeInfo* auth_info,
+ net::URLRequest* request);
+
+ // Initializes the underlying platform specific view.
+ virtual void BuildViewForPasswordManager(PasswordManager* manager,
+ std::wstring explanation) = 0;
+
+ // Sets information about the authentication type (|form|) and the
+ // |password_manager| for this profile.
+ void SetPasswordForm(const webkit_glue::PasswordForm& form);
+ void SetPasswordManager(PasswordManager* password_manager);
+
+ // Returns the TabContents that needs authentication.
+ TabContents* GetTabContentsForLogin() const;
+
+ // Resend the request with authentication credentials.
+ // This function can be called from either thread.
+ void SetAuth(const std::wstring& username, const std::wstring& password);
+
+ // Display the error page without asking for credentials again.
+ // This function can be called from either thread.
+ void CancelAuth();
+
+ // Notify the handler that the request was cancelled.
+ // This function can only be called from the IO thread.
+ void OnRequestCancelled();
+
+ // 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.
+ 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.
+ bool WasAuthHandled(bool set_handled);
+
+ // Calls SetAuth from the IO loop.
+ void SetAuthDeferred(const std::wstring& username,
+ const std::wstring& password);
+
+ // Calls CancelAuth from the IO loop.
+ void CancelAuthDeferred();
+
+ // Closes the view_contents from the UI loop.
+ void CloseContentsDeferred();
+
+ // True if we've handled auth (SetAuth or CancelAuth has been called).
+ bool handled_auth_;
+ Lock handled_auth_lock_;
+
+ // The ConstrainedWindow that is hosting our LoginView.
+ // 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.
+ net::URLRequest* request_;
+
+ // The PasswordForm sent to the PasswordManager. This is so we can refer to it
+ // when later notifying the password manager if the credentials were accepted
+ // or rejected.
+ // This should only be accessed on the UI loop.
+ webkit_glue::PasswordForm password_form_;
+
+ // Points to the password manager owned by the TabContents requesting auth.
+ // Can be null if the TabContents is not a TabContents.
+ // This should only be accessed on the UI loop.
+ PasswordManager* password_manager_;
+
+ // Cached from the net::URLRequest, in case it goes NULL on us.
+ int render_process_host_id_;
+ int tab_contents_id_;
+
+ // 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
+// for testing.
+class LoginNotificationDetails {
+ public:
+ explicit LoginNotificationDetails(LoginHandler* handler)
+ : handler_(handler) {}
+ LoginHandler* handler() const { return handler_; }
+
+ private:
+ LoginNotificationDetails() {}
+
+ LoginHandler* handler_; // Where to send the response.
+
+ 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
+// net::URLRequest::Delegate::OnAuthRequired. The prompt will be created
+// on the main UI thread via a call to UI loop's InvokeLater, and will send the
+// credentials back to the net::URLRequest on the calling thread.
+// A LoginHandler object (which lives on the calling thread) is returned,
+// which can be used to set or cancel authentication programmatically. The
+// caller must invoke OnRequestCancelled() on this LoginHandler before
+// destroying the net::URLRequest.
+LoginHandler* CreateLoginPrompt(net::AuthChallengeInfo* auth_info,
+ net::URLRequest* request);
+
+// Helper to remove the ref from an net::URLRequest to the LoginHandler.
+// Should only be called from the IO thread, since it accesses an
+// net::URLRequest.
+void ResetLoginHandlerForRequest(net::URLRequest* request);
+
+// Get the signon_realm under which the identity should be saved.
+std::string GetSignonRealm(const GURL& url,
+ const net::AuthChallengeInfo& auth_info);
+
+#endif // CHROME_BROWSER_UI_LOGIN_LOGIN_PROMPT_H_
diff --git a/chrome/browser/ui/login/login_prompt_gtk.cc b/chrome/browser/ui/login/login_prompt_gtk.cc
new file mode 100644
index 0000000..92da74f
--- /dev/null
+++ b/chrome/browser/ui/login/login_prompt_gtk.cc
@@ -0,0 +1,199 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/login/login_prompt.h"
+
+#include <gtk/gtk.h>
+
+#include "app/l10n_util.h"
+#include "app/gtk_signal.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/browser_thread.h"
+#include "chrome/browser/gtk/constrained_window_gtk.h"
+#include "chrome/browser/gtk/gtk_util.h"
+#include "chrome/browser/password_manager/password_manager.h"
+#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
+#include "chrome/browser/tab_contents/navigation_controller.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/tab_contents/tab_contents_delegate.h"
+#include "chrome/browser/tab_contents/tab_contents_view_gtk.h"
+#include "chrome/browser/tab_contents/tab_util.h"
+#include "chrome/browser/ui/login/login_model.h"
+#include "chrome/common/notification_service.h"
+#include "grit/generated_resources.h"
+#include "net/url_request/url_request.h"
+
+using webkit_glue::PasswordForm;
+
+// ----------------------------------------------------------------------------
+// LoginHandlerGtk
+
+// This class simply forwards the authentication from the LoginView (on
+// the UI thread) to the net::URLRequest (on the I/O thread).
+// This class uses ref counting to ensure that it lives until all InvokeLaters
+// have been called.
+class LoginHandlerGtk : public LoginHandler,
+ public ConstrainedWindowGtkDelegate {
+ public:
+ LoginHandlerGtk(net::AuthChallengeInfo* auth_info, net::URLRequest* request)
+ : LoginHandler(auth_info, request),
+ username_entry_(NULL),
+ password_entry_(NULL),
+ ok_(NULL) {
+ }
+
+ virtual ~LoginHandlerGtk() {
+ root_.Destroy();
+ }
+
+ // LoginModelObserver implementation.
+ virtual void OnAutofillDataAvailable(const std::wstring& username,
+ const std::wstring& password) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ // NOTE: Would be nice to use gtk_entry_get_text_length, but it is fairly
+ // new and not always in our GTK version.
+ if (strlen(gtk_entry_get_text(GTK_ENTRY(username_entry_))) == 0) {
+ gtk_entry_set_text(GTK_ENTRY(username_entry_),
+ WideToUTF8(username).c_str());
+ gtk_entry_set_text(GTK_ENTRY(password_entry_),
+ WideToUTF8(password).c_str());
+ gtk_editable_select_region(GTK_EDITABLE(username_entry_), 0, -1);
+ }
+ }
+
+ // LoginHandler:
+ virtual void BuildViewForPasswordManager(PasswordManager* manager,
+ std::wstring explanation) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ root_.Own(gtk_vbox_new(FALSE, gtk_util::kContentAreaBorder));
+ GtkWidget* label = gtk_label_new(WideToUTF8(explanation).c_str());
+ gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
+ gtk_box_pack_start(GTK_BOX(root_.get()), label, FALSE, FALSE, 0);
+
+ username_entry_ = gtk_entry_new();
+ gtk_entry_set_activates_default(GTK_ENTRY(username_entry_), TRUE);
+
+ password_entry_ = gtk_entry_new();
+ gtk_entry_set_activates_default(GTK_ENTRY(password_entry_), TRUE);
+ gtk_entry_set_visibility(GTK_ENTRY(password_entry_), FALSE);
+
+ GtkWidget* table = gtk_util::CreateLabeledControlsGroup(NULL,
+ l10n_util::GetStringUTF8(IDS_LOGIN_DIALOG_USERNAME_FIELD).c_str(),
+ username_entry_,
+ l10n_util::GetStringUTF8(IDS_LOGIN_DIALOG_PASSWORD_FIELD).c_str(),
+ password_entry_,
+ NULL);
+ gtk_box_pack_start(GTK_BOX(root_.get()), table, FALSE, FALSE, 0);
+
+ GtkWidget* hbox = gtk_hbox_new(FALSE, 12);
+ gtk_box_pack_start(GTK_BOX(root_.get()), hbox, FALSE, FALSE, 0);
+
+ ok_ = gtk_button_new_from_stock(GTK_STOCK_OK);
+ gtk_button_set_label(
+ GTK_BUTTON(ok_),
+ l10n_util::GetStringUTF8(IDS_LOGIN_DIALOG_OK_BUTTON_LABEL).c_str());
+ g_signal_connect(ok_, "clicked", G_CALLBACK(OnOKClickedThunk), this);
+ gtk_box_pack_end(GTK_BOX(hbox), ok_, FALSE, FALSE, 0);
+
+ GtkWidget* cancel = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
+ g_signal_connect(cancel, "clicked", G_CALLBACK(OnCancelClickedThunk), this);
+ gtk_box_pack_end(GTK_BOX(hbox), cancel, FALSE, FALSE, 0);
+
+ g_signal_connect(root_.get(), "hierarchy-changed",
+ G_CALLBACK(OnPromptHierarchyChangedThunk), this);
+
+ SetModel(manager);
+
+ // Scary thread safety note: This can potentially be called *after* SetAuth
+ // or CancelAuth (say, if the request was cancelled before the UI thread got
+ // control). However, that's OK since any UI interaction in those functions
+ // 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));
+
+ NotifyAuthNeeded();
+ }
+
+ // Overridden from ConstrainedWindowGtkDelegate:
+ virtual GtkWidget* GetWidgetRoot() {
+ return root_.get();
+ }
+
+ virtual void DeleteDelegate() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ // The constrained window is going to delete itself; clear our pointer.
+ SetDialog(NULL);
+ SetModel(NULL);
+
+ ReleaseSoon();
+ }
+
+ private:
+ friend class LoginPrompt;
+
+ CHROMEGTK_CALLBACK_0(LoginHandlerGtk, void, OnOKClicked);
+ CHROMEGTK_CALLBACK_0(LoginHandlerGtk, void, OnCancelClicked);
+ CHROMEGTK_CALLBACK_1(LoginHandlerGtk, void, OnPromptHierarchyChanged,
+ GtkWidget*);
+
+ // The GtkWidgets that form our visual hierarchy:
+ // The root container we pass to our parent.
+ OwnedWidgetGtk root_;
+
+ // GtkEntry widgets that the user types into.
+ GtkWidget* username_entry_;
+ GtkWidget* password_entry_;
+ GtkWidget* ok_;
+
+ DISALLOW_COPY_AND_ASSIGN(LoginHandlerGtk);
+};
+
+void LoginHandlerGtk::OnOKClicked(GtkWidget* sender) {
+ SetAuth(
+ UTF8ToWide(gtk_entry_get_text(GTK_ENTRY(username_entry_))),
+ UTF8ToWide(gtk_entry_get_text(GTK_ENTRY(password_entry_))));
+}
+
+void LoginHandlerGtk::OnCancelClicked(GtkWidget* sender) {
+ CancelAuth();
+}
+
+void LoginHandlerGtk::OnPromptHierarchyChanged(GtkWidget* sender,
+ GtkWidget* previous_toplevel) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ if (!GTK_WIDGET_TOPLEVEL(gtk_widget_get_toplevel(ok_)))
+ return;
+
+ // Now that we have attached ourself to the window, we can make our OK
+ // button the default action and mess with the focus.
+ GTK_WIDGET_SET_FLAGS(ok_, GTK_CAN_DEFAULT);
+ gtk_widget_grab_default(ok_);
+
+ TabContents* contents = GetTabContentsForLogin();
+
+ // The user may have focused another tab. In this case do not grab focus
+ // until this tab is refocused.
+ if ((!contents->delegate() ||
+ contents->delegate()->ShouldFocusConstrainedWindow()) &&
+ gtk_util::IsWidgetAncestryVisible(username_entry_)) {
+ gtk_widget_grab_focus(username_entry_);
+ } else {
+ // TODO(estade): this define should not need to be here because this class
+ // should not be used on linux/views.
+#if defined(TOOLKIT_GTK)
+ static_cast<TabContentsViewGtk*>(contents->view())->
+ SetFocusedWidget(username_entry_);
+#endif
+ }
+}
+
+// static
+LoginHandler* LoginHandler::Create(net::AuthChallengeInfo* auth_info,
+ net::URLRequest* request) {
+ return new LoginHandlerGtk(auth_info, request);
+}
diff --git a/chrome/browser/ui/login/login_prompt_mac.h b/chrome/browser/ui/login/login_prompt_mac.h
new file mode 100644
index 0000000..bf0677d
--- /dev/null
+++ b/chrome/browser/ui/login/login_prompt_mac.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_LOGIN_LOGIN_PROMPT_MAC_H_
+#define CHROME_BROWSER_UI_LOGIN_LOGIN_PROMPT_MAC_H_
+#pragma once
+
+#import <Cocoa/Cocoa.h>
+
+class LoginHandlerMac;
+
+// Controller of the sheet used by LoginHandlerMac. Interface Builder wants
+// this to be in a .h file.
+@interface LoginHandlerSheet : NSWindowController {
+ @private
+ IBOutlet NSTextField* nameField_;
+ IBOutlet NSSecureTextField* passwordField_;
+ IBOutlet NSTextField* explanationField_;
+ IBOutlet NSButton* loginButton_;
+ IBOutlet NSButton* cancelButton_;
+ LoginHandlerMac* handler_; // weak, owns us
+}
+- (id)initWithLoginHandler:(LoginHandlerMac*)handler;
+- (IBAction)loginPressed:(id)sender;
+- (IBAction)cancelPressed:(id)sender;
+- (void)sheetDidEnd:(NSWindow*)sheet
+ returnCode:(int)returnCode
+ contextInfo:(void*)contextInfo;
+- (void)autofillLogin:(NSString*)login password:(NSString*)password;
+- (void)setExplanation:(NSString*)explanation;
+@end
+
+#endif // CHROME_BROWSER_UI_LOGIN_LOGIN_PROMPT_MAC_H_
diff --git a/chrome/browser/ui/login/login_prompt_mac.mm b/chrome/browser/ui/login/login_prompt_mac.mm
new file mode 100644
index 0000000..d2f28d2
--- /dev/null
+++ b/chrome/browser/ui/login/login_prompt_mac.mm
@@ -0,0 +1,191 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/login/login_prompt.h"
+#import "chrome/browser/ui/login/login_prompt_mac.h"
+
+#include "app/l10n_util.h"
+#include "base/mac_util.h"
+#include "base/string_util.h"
+#include "base/sys_string_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/browser_thread.h"
+#include "chrome/browser/password_manager/password_manager.h"
+#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
+#include "chrome/browser/tab_contents/navigation_controller.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/tab_contents/tab_util.h"
+#include "chrome/browser/ui/cocoa/constrained_window_mac.h"
+#include "chrome/browser/ui/login/login_model.h"
+#include "chrome/common/notification_service.h"
+#include "grit/generated_resources.h"
+#include "net/url_request/url_request.h"
+#include "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h"
+
+using webkit_glue::PasswordForm;
+
+// ----------------------------------------------------------------------------
+// LoginHandlerMac
+
+// This class simply forwards the authentication from the LoginView (on
+// the UI thread) to the net::URLRequest (on the I/O thread).
+// This class uses ref counting to ensure that it lives until all InvokeLaters
+// have been called.
+class LoginHandlerMac : public LoginHandler,
+ public ConstrainedWindowMacDelegateCustomSheet {
+ public:
+ LoginHandlerMac(net::AuthChallengeInfo* auth_info, net::URLRequest* request)
+ : LoginHandler(auth_info, request),
+ sheet_controller_(nil) {
+ }
+
+ virtual ~LoginHandlerMac() {
+ }
+
+ // LoginModelObserver implementation.
+ virtual void OnAutofillDataAvailable(const std::wstring& username,
+ const std::wstring& password) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ [sheet_controller_ autofillLogin:base::SysWideToNSString(username)
+ password:base::SysWideToNSString(password)];
+ }
+
+ // LoginHandler:
+ virtual void BuildViewForPasswordManager(PasswordManager* manager,
+ std::wstring explanation) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ // Load nib here instead of in constructor.
+ sheet_controller_ = [[[LoginHandlerSheet alloc]
+ initWithLoginHandler:this] autorelease];
+ init([sheet_controller_ window], sheet_controller_,
+ @selector(sheetDidEnd:returnCode:contextInfo:));
+
+ SetModel(manager);
+
+ [sheet_controller_ setExplanation:base::SysWideToNSString(explanation)];
+
+ // Scary thread safety note: This can potentially be called *after* SetAuth
+ // or CancelAuth (say, if the request was cancelled before the UI thread got
+ // control). However, that's OK since any UI interaction in those functions
+ // 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));
+
+ NotifyAuthNeeded();
+ }
+
+ // Overridden from ConstrainedWindowMacDelegate:
+ virtual void DeleteDelegate() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ // The constrained window is going to delete itself; clear our pointer.
+ SetDialog(NULL);
+ SetModel(NULL);
+
+ // Close sheet if it's still open, as required by
+ // ConstrainedWindowMacDelegate.
+ if (is_sheet_open())
+ [NSApp endSheet:sheet()];
+
+ ReleaseSoon();
+ }
+
+ void OnLoginPressed(const std::wstring& username,
+ const std::wstring& password) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ SetAuth(username, password);
+ }
+
+ void OnCancelPressed() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ CancelAuth();
+ }
+
+ private:
+ friend class LoginPrompt;
+
+ // The Cocoa controller of the GUI.
+ LoginHandlerSheet* sheet_controller_;
+
+ DISALLOW_COPY_AND_ASSIGN(LoginHandlerMac);
+};
+
+// static
+LoginHandler* LoginHandler::Create(net::AuthChallengeInfo* auth_info,
+ net::URLRequest* request) {
+ return new LoginHandlerMac(auth_info, request);
+}
+
+// ----------------------------------------------------------------------------
+// LoginHandlerSheet
+
+@implementation LoginHandlerSheet
+
+- (id)initWithLoginHandler:(LoginHandlerMac*)handler {
+ NSString* nibPath =
+ [mac_util::MainAppBundle() pathForResource:@"HttpAuthLoginSheet"
+ ofType:@"nib"];
+ if ((self = [super initWithWindowNibPath:nibPath
+ owner:self])) {
+ handler_ = handler;
+ }
+ return self;
+}
+
+- (void)dealloc {
+ // The buttons could be in a modal loop, so disconnect them so they cannot
+ // call back to us after we're dead.
+ [loginButton_ setTarget:nil];
+ [cancelButton_ setTarget:nil];
+ [super dealloc];
+}
+
+- (IBAction)loginPressed:(id)sender {
+ using base::SysNSStringToWide;
+ [NSApp endSheet:[self window]];
+ handler_->OnLoginPressed(SysNSStringToWide([nameField_ stringValue]),
+ SysNSStringToWide([passwordField_ stringValue]));
+}
+
+- (IBAction)cancelPressed:(id)sender {
+ [NSApp endSheet:[self window]];
+ handler_->OnCancelPressed();
+}
+
+- (void)sheetDidEnd:(NSWindow*)sheet
+ returnCode:(int)returnCode
+ contextInfo:(void *)contextInfo {
+ [sheet orderOut:self];
+ // Also called when user navigates to another page while the sheet is open.
+}
+
+- (void)autofillLogin:(NSString*)login password:(NSString*)password {
+ if ([[nameField_ stringValue] length] == 0) {
+ [nameField_ setStringValue:login];
+ [passwordField_ setStringValue:password];
+ [nameField_ selectText:self];
+ }
+}
+
+- (void)setExplanation:(NSString*)explanation {
+ // Put in the text.
+ [explanationField_ setStringValue:explanation];
+
+ // Resize the TextField.
+ CGFloat explanationShift =
+ [GTMUILocalizerAndLayoutTweaker
+ sizeToFitFixedWidthTextField:explanationField_];
+
+ // Resize the window (no shifting needed due to window layout).
+ NSSize windowDelta = NSMakeSize(0, explanationShift);
+ [GTMUILocalizerAndLayoutTweaker
+ resizeWindowWithoutAutoResizingSubViews:[self window]
+ delta:windowDelta];
+}
+
+@end
diff --git a/chrome/browser/ui/login/login_prompt_uitest.cc b/chrome/browser/ui/login/login_prompt_uitest.cc
new file mode 100644
index 0000000..bdda3fd
--- /dev/null
+++ b/chrome/browser/ui/login/login_prompt_uitest.cc
@@ -0,0 +1,280 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "chrome/browser/net/url_fixer_upper.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/test/automation/tab_proxy.h"
+#include "chrome/test/automation/browser_proxy.h"
+#include "chrome/test/ui/ui_test.h"
+#include "net/test/test_server.h"
+
+using std::wstring;
+
+namespace {
+
+const FilePath::CharType kDocRoot[] = FILE_PATH_LITERAL("chrome/test/data");
+
+} // namespace
+
+class LoginPromptTest : public UITest {
+ protected:
+ LoginPromptTest()
+ : username_basic_(L"basicuser"),
+ username_digest_(L"digestuser"),
+ password_(L"secret"),
+ password_bad_(L"denyme"),
+ test_server_(net::TestServer::TYPE_HTTP, FilePath(kDocRoot)) {
+ }
+
+ void AppendTab(const GURL& url) {
+ scoped_refptr<BrowserProxy> window_proxy(automation()->GetBrowserWindow(0));
+ ASSERT_TRUE(window_proxy.get());
+ ASSERT_TRUE(window_proxy->AppendTab(url));
+ }
+
+ protected:
+ wstring username_basic_;
+ wstring username_digest_;
+ wstring password_;
+ wstring password_bad_;
+
+ net::TestServer test_server_;
+};
+
+wstring ExpectedTitleFromAuth(const wstring& username,
+ const wstring& password) {
+ // The TestServer sets the title to username/password on successful login.
+ return username + L"/" + password;
+}
+
+#if defined(OS_WIN)
+// Probably related to test server flakiness in http://crbug.com/60937
+#define MAYBE_TestBasicAuth FLAKY_TestBasicAuth
+#else
+#define MAYBE_TestBasicAuth TestBasicAuth
+#endif
+
+// Test that "Basic" HTTP authentication works.
+TEST_F(LoginPromptTest, MAYBE_TestBasicAuth) {
+ ASSERT_TRUE(test_server_.Start());
+
+ scoped_refptr<TabProxy> tab(GetActiveTab());
+ ASSERT_TRUE(tab.get());
+ ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED,
+ tab->NavigateToURL(test_server_.GetURL("auth-basic")));
+
+ EXPECT_TRUE(tab->NeedsAuth());
+ EXPECT_FALSE(tab->SetAuth(username_basic_, password_bad_));
+ EXPECT_TRUE(tab->NeedsAuth());
+ EXPECT_TRUE(tab->CancelAuth());
+ EXPECT_EQ(L"Denied: wrong password", GetActiveTabTitle());
+
+ ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED,
+ tab->NavigateToURL(test_server_.GetURL("auth-basic")));
+
+ EXPECT_TRUE(tab->NeedsAuth());
+ EXPECT_TRUE(tab->SetAuth(username_basic_, password_));
+ EXPECT_EQ(ExpectedTitleFromAuth(username_basic_, password_),
+ GetActiveTabTitle());
+}
+
+#if defined(OS_WIN)
+// Probably related to test server flakiness in http://crbug.com/60937
+#define MAYBE_TestDigestAuth FLAKY_TestDigestAuth
+#else
+#define MAYBE_TestDigestAuth TestDigestAuth
+#endif
+
+// Test that "Digest" HTTP authentication works.
+TEST_F(LoginPromptTest, MAYBE_TestDigestAuth) {
+ ASSERT_TRUE(test_server_.Start());
+
+ scoped_refptr<TabProxy> tab(GetActiveTab());
+ ASSERT_TRUE(tab.get());
+ ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED,
+ tab->NavigateToURL(test_server_.GetURL("auth-digest")));
+
+ EXPECT_TRUE(tab->NeedsAuth());
+ EXPECT_FALSE(tab->SetAuth(username_digest_, password_bad_));
+ EXPECT_TRUE(tab->CancelAuth());
+ EXPECT_EQ(L"Denied: wrong password", GetActiveTabTitle());
+
+ ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED,
+ tab->NavigateToURL(test_server_.GetURL("auth-digest")));
+
+ EXPECT_TRUE(tab->NeedsAuth());
+ EXPECT_TRUE(tab->SetAuth(username_digest_, password_));
+ EXPECT_EQ(ExpectedTitleFromAuth(username_digest_, password_),
+ GetActiveTabTitle());
+}
+
+#if defined(OS_WIN)
+// Probably related to test server flakiness in http://crbug.com/60937
+#define MAYBE_TestTwoAuths FLAKY_TestTwoAuths
+#else
+#define MAYBE_TestTwoAuths TestTwoAuths
+#endif
+
+// Test that logging in on 2 tabs at once works.
+TEST_F(LoginPromptTest, MAYBE_TestTwoAuths) {
+ ASSERT_TRUE(test_server_.Start());
+
+ scoped_refptr<TabProxy> basic_tab(GetActiveTab());
+ ASSERT_TRUE(basic_tab.get());
+ ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED,
+ basic_tab->NavigateToURL(test_server_.GetURL("auth-basic")));
+
+ AppendTab(GURL(chrome::kAboutBlankURL));
+ scoped_refptr<TabProxy> digest_tab(GetActiveTab());
+ ASSERT_TRUE(digest_tab.get());
+ ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED,
+ digest_tab->NavigateToURL(test_server_.GetURL("auth-digest")));
+
+ EXPECT_TRUE(basic_tab->NeedsAuth());
+ EXPECT_TRUE(basic_tab->SetAuth(username_basic_, password_));
+ EXPECT_TRUE(digest_tab->NeedsAuth());
+ EXPECT_TRUE(digest_tab->SetAuth(username_digest_, password_));
+
+ wstring title;
+ EXPECT_TRUE(basic_tab->GetTabTitle(&title));
+ EXPECT_EQ(ExpectedTitleFromAuth(username_basic_, password_), title);
+
+ EXPECT_TRUE(digest_tab->GetTabTitle(&title));
+ EXPECT_EQ(ExpectedTitleFromAuth(username_digest_, password_), title);
+}
+
+#if defined(OS_WIN)
+// Probably related to test server flakiness in http://crbug.com/60937
+#define MAYBE_TestCancelAuth FLAKY_TestCancelAuth
+#else
+#define MAYBE_TestCancelAuth TestCancelAuth
+#endif
+
+// Test that cancelling authentication works.
+TEST_F(LoginPromptTest, MAYBE_TestCancelAuth) {
+ ASSERT_TRUE(test_server_.Start());
+
+ scoped_refptr<TabProxy> tab(GetActiveTab());
+ ASSERT_TRUE(tab.get());
+
+ // First navigate to a test server page so we have something to go back to.
+ ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
+ tab->NavigateToURL(test_server_.GetURL("a")));
+
+ // Navigating while auth is requested is the same as cancelling.
+ ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED,
+ tab->NavigateToURL(test_server_.GetURL("auth-basic")));
+ EXPECT_TRUE(tab->NeedsAuth());
+ ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
+ tab->NavigateToURL(test_server_.GetURL("b")));
+ EXPECT_FALSE(tab->NeedsAuth());
+
+ ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED,
+ tab->NavigateToURL(test_server_.GetURL("auth-basic")));
+ EXPECT_TRUE(tab->NeedsAuth());
+ EXPECT_TRUE(tab->GoBack()); // should bring us back to 'a'
+ EXPECT_FALSE(tab->NeedsAuth());
+
+ // Now add a page and go back, so we have something to go forward to.
+ ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
+ tab->NavigateToURL(test_server_.GetURL("c")));
+ EXPECT_TRUE(tab->GoBack()); // should bring us back to 'a'
+
+ ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED,
+ tab->NavigateToURL(test_server_.GetURL("auth-basic")));
+ EXPECT_TRUE(tab->NeedsAuth());
+ EXPECT_TRUE(tab->GoForward()); // should bring us to 'c'
+ EXPECT_FALSE(tab->NeedsAuth());
+
+ // Now test that cancelling works as expected.
+ ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED,
+ tab->NavigateToURL(test_server_.GetURL("auth-basic")));
+ EXPECT_TRUE(tab->NeedsAuth());
+ EXPECT_TRUE(tab->CancelAuth());
+ EXPECT_FALSE(tab->NeedsAuth());
+ EXPECT_EQ(L"Denied: no auth", GetActiveTabTitle());
+}
+
+#if defined(OS_WIN)
+// Probably related to test server flakiness in http://crbug.com/60937
+#define MAYBE_SupplyRedundantAuths FLAKY_SupplyRedundantAuths
+#else
+#define MAYBE_SupplyRedundantAuths SupplyRedundantAuths
+#endif
+
+// If multiple tabs are looking for the same auth, the user should only have to
+// enter it once.
+TEST_F(LoginPromptTest, MAYBE_SupplyRedundantAuths) {
+ ASSERT_TRUE(test_server_.Start());
+
+ scoped_refptr<TabProxy> basic_tab1(GetActiveTab());
+ ASSERT_TRUE(basic_tab1.get());
+ ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED,
+ basic_tab1->NavigateToURL(test_server_.GetURL("auth-basic/1")));
+ EXPECT_TRUE(basic_tab1->NeedsAuth());
+
+ AppendTab(GURL(chrome::kAboutBlankURL));
+ scoped_refptr<TabProxy> basic_tab2(GetActiveTab());
+ ASSERT_TRUE(basic_tab2.get());
+ ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED,
+ basic_tab2->NavigateToURL(test_server_.GetURL("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 defined(OS_WIN)
+// Probably related to test server flakiness in http://crbug.com/60937
+#define MAYBE_CancelRedundantAuths FLAKY_CancelRedundantAuths
+#else
+#define MAYBE_CancelRedundantAuths CancelRedundantAuths
+#endif
+
+// If multiple tabs are looking for the same auth, and one is cancelled, the
+// other should be cancelled as well.
+TEST_F(LoginPromptTest, MAYBE_CancelRedundantAuths) {
+ ASSERT_TRUE(test_server_.Start());
+
+ scoped_refptr<TabProxy> basic_tab1(GetActiveTab());
+ ASSERT_TRUE(basic_tab1.get());
+ ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED,
+ basic_tab1->NavigateToURL(test_server_.GetURL("auth-basic/1")));
+ EXPECT_TRUE(basic_tab1->NeedsAuth());
+
+ AppendTab(GURL(chrome::kAboutBlankURL));
+ scoped_refptr<TabProxy> basic_tab2(GetActiveTab());
+ ASSERT_TRUE(basic_tab2.get());
+ ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED,
+ basic_tab2->NavigateToURL(test_server_.GetURL("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/ui/login/login_prompt_unittest.cc b/chrome/browser/ui/login/login_prompt_unittest.cc
new file mode 100644
index 0000000..9454e13
--- /dev/null
+++ b/chrome/browser/ui/login/login_prompt_unittest.cc
@@ -0,0 +1,42 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/login/login_prompt.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/auth.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+
+TEST(LoginPromptTest, GetSignonRealm) {
+ scoped_refptr<net::AuthChallengeInfo> auth_info = new net::AuthChallengeInfo;
+ auth_info->is_proxy = false; // server auth
+ // auth_info->host is intentionally left empty.
+ auth_info->scheme = L"Basic";
+ auth_info->realm = L"WallyWorld";
+
+ std::string url[] = {
+ "https://www.nowhere.org/dir/index.html",
+ "https://www.nowhere.org:443/dir/index.html", // default port
+ "https://www.nowhere.org:8443/dir/index.html", // non-default port
+ "https://www.nowhere.org", // no trailing slash
+ "https://foo:bar@www.nowhere.org/dir/index.html", // username:password
+ "https://www.nowhere.org/dir/index.html?id=965362", // query
+ "https://www.nowhere.org/dir/index.html#toc", // reference
+ };
+
+ std::string expected[] = {
+ "https://www.nowhere.org/WallyWorld",
+ "https://www.nowhere.org/WallyWorld",
+ "https://www.nowhere.org:8443/WallyWorld",
+ "https://www.nowhere.org/WallyWorld",
+ "https://www.nowhere.org/WallyWorld",
+ "https://www.nowhere.org/WallyWorld",
+ "https://www.nowhere.org/WallyWorld"
+ };
+
+ for (size_t i = 0; i < arraysize(url); i++) {
+ std::string key = GetSignonRealm(GURL(url[i]), *auth_info);
+ EXPECT_EQ(expected[i], key);
+ }
+}
diff --git a/chrome/browser/ui/login/login_prompt_win.cc b/chrome/browser/ui/login/login_prompt_win.cc
new file mode 100644
index 0000000..d73954b
--- /dev/null
+++ b/chrome/browser/ui/login/login_prompt_win.cc
@@ -0,0 +1,147 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/login/login_prompt.h"
+
+#include "app/l10n_util.h"
+#include "chrome/browser/browser_thread.h"
+#include "chrome/browser/password_manager/password_manager.h"
+#include "chrome/browser/renderer_host/render_process_host.h"
+#include "chrome/browser/renderer_host/render_view_host.h"
+#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
+#include "chrome/browser/tab_contents/navigation_controller.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/tab_contents/tab_contents_delegate.h"
+#include "chrome/browser/tab_contents/tab_util.h"
+#include "chrome/browser/ui/views/login_view.h"
+#include "chrome/common/notification_service.h"
+#include "grit/generated_resources.h"
+#include "net/url_request/url_request.h"
+#include "views/window/dialog_delegate.h"
+
+using webkit_glue::PasswordForm;
+
+// ----------------------------------------------------------------------------
+// LoginHandlerWin
+
+// This class simply forwards the authentication from the LoginView (on
+// the UI thread) to the net::URLRequest (on the I/O thread).
+// This class uses ref counting to ensure that it lives until all InvokeLaters
+// have been called.
+class LoginHandlerWin : public LoginHandler,
+ public ConstrainedDialogDelegate {
+ public:
+ LoginHandlerWin(net::AuthChallengeInfo* auth_info, net::URLRequest* request)
+ : LoginHandler(auth_info, request) {
+ }
+
+ // LoginModelObserver implementation.
+ virtual void OnAutofillDataAvailable(const std::wstring& username,
+ const std::wstring& password) {
+ // Nothing to do here since LoginView takes care of autofil for win.
+ }
+
+ void set_login_view(LoginView* login_view) {
+ login_view_ = login_view;
+ }
+
+ // views::DialogDelegate methods:
+ virtual std::wstring GetDialogButtonLabel(
+ MessageBoxFlags::DialogButton button) const {
+ if (button == MessageBoxFlags::DIALOGBUTTON_OK)
+ return l10n_util::GetString(IDS_LOGIN_DIALOG_OK_BUTTON_LABEL);
+ return DialogDelegate::GetDialogButtonLabel(button);
+ }
+
+ virtual std::wstring GetWindowTitle() const {
+ return l10n_util::GetString(IDS_LOGIN_DIALOG_TITLE);
+ }
+
+ virtual void WindowClosing() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ TabContents* tab = GetTabContentsForLogin();
+ if (tab)
+ tab->render_view_host()->set_ignore_input_events(false);
+
+ // Reference is no longer valid.
+ SetDialog(NULL);
+
+ CancelAuth();
+ }
+
+ virtual void DeleteDelegate() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ // The constrained window is going to delete itself; clear our pointer.
+ SetDialog(NULL);
+ SetModel(NULL);
+
+ ReleaseSoon();
+ }
+
+ virtual bool Cancel() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ CancelAuth();
+ return true;
+ }
+
+ virtual bool Accept() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ SetAuth(login_view_->GetUsername(), login_view_->GetPassword());
+ return true;
+ }
+
+ virtual views::View* GetContentsView() {
+ return login_view_;
+ }
+
+ // LoginHandler:
+
+ virtual void BuildViewForPasswordManager(PasswordManager* manager,
+ std::wstring explanation) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ TabContents* tab_contents = GetTabContentsForLogin();
+ bool should_focus_view = !tab_contents->delegate() ||
+ tab_contents->delegate()->ShouldFocusConstrainedWindow();
+
+ LoginView* view = new LoginView(explanation, should_focus_view);
+
+ // Set the model for the login view. The model (password manager) is owned
+ // by the view's parent TabContents, so natural destruction order means we
+ // don't have to worry about calling SetModel(NULL), because the view will
+ // be deleted before the password manager.
+ view->SetModel(manager);
+
+ set_login_view(view);
+
+ // Scary thread safety note: This can potentially be called *after* SetAuth
+ // or CancelAuth (say, if the request was cancelled before the UI thread got
+ // control). However, that's OK since any UI interaction in those functions
+ // 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));
+ NotifyAuthNeeded();
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<LoginHandlerWin>;
+ friend class LoginPrompt;
+
+ ~LoginHandlerWin() {}
+
+ // The LoginView that contains the user's login information
+ LoginView* login_view_;
+
+ DISALLOW_COPY_AND_ASSIGN(LoginHandlerWin);
+};
+
+// static
+LoginHandler* LoginHandler::Create(net::AuthChallengeInfo* auth_info,
+ net::URLRequest* request) {
+ return new LoginHandlerWin(auth_info, request);
+}