summaryrefslogtreecommitdiffstats
path: root/chrome/browser/login_prompt.cc
diff options
context:
space:
mode:
authorinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
committerinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
commit09911bf300f1a419907a9412154760efd0b7abc3 (patch)
treef131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/browser/login_prompt.cc
parent586acc5fe142f498261f52c66862fa417c3d52d2 (diff)
downloadchromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/login_prompt.cc')
-rw-r--r--chrome/browser/login_prompt.cc406
1 files changed, 406 insertions, 0 deletions
diff --git a/chrome/browser/login_prompt.cc b/chrome/browser/login_prompt.cc
new file mode 100644
index 0000000..d0f6248
--- /dev/null
+++ b/chrome/browser/login_prompt.cc
@@ -0,0 +1,406 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/browser/login_prompt.h"
+
+#include "base/atomic.h"
+#include "base/command_line.h"
+#include "base/message_loop.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/constrained_window.h"
+#include "chrome/browser/controller.h"
+#include "chrome/browser/navigation_controller.h"
+#include "chrome/browser/password_manager.h"
+#include "chrome/browser/render_process_host.h"
+#include "chrome/browser/resource_dispatcher_host.h"
+#include "chrome/browser/web_contents.h"
+#include "chrome/browser/tab_util.h"
+#include "chrome/browser/views/login_view.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/views/dialog_delegate.h"
+#include "net/base/auth.h"
+#include "net/url_request/url_request.h"
+
+#include "generated_resources.h"
+
+using namespace std;
+using ChromeViews::LoginView;
+
+class LoginHandlerImpl;
+
+// Helper to remove the ref from an URLRequest to the LoginHandler.
+// Should only be called from the IO thread, since it accesses an URLRequest.
+static void ResetLoginHandlerForRequest(URLRequest* request) {
+ ResourceDispatcherHost::ExtraRequestInfo* info =
+ ResourceDispatcherHost::ExtraInfoForRequest(request);
+ if (!info)
+ return;
+
+ info->login_handler = NULL;
+}
+
+// ----------------------------------------------------------------------------
+// LoginHandlerImpl
+
+// This class simply forwards the authentication from the LoginView (on
+// the UI thread) to the URLRequest (on the I/O thread).
+// This class uses ref counting to ensure that it lives until all InvokeLaters
+// have been called.
+class LoginHandlerImpl : public LoginHandler,
+ public base::RefCountedThreadSafe<LoginHandlerImpl>,
+ public ChromeViews::DialogDelegate {
+ public:
+ LoginHandlerImpl(URLRequest* request, MessageLoop* ui_loop)
+ : dialog_(NULL),
+ got_auth_(FALSE),
+ request_(request),
+ request_loop_(MessageLoop::current()),
+ ui_loop_(ui_loop),
+ password_manager_(NULL) {
+ DCHECK(request_) << "LoginHandler constructed with NULL request";
+
+ AddRef(); // matched by ReleaseLater.
+ if (!tab_util::GetTabContentsID(request_, &render_process_host_id_,
+ &tab_contents_id_)) {
+ NOTREACHED();
+ }
+ }
+
+ ~LoginHandlerImpl() {
+ }
+
+ // Initialize the UI part of the LoginHandler.
+ // 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).
+ void InitWithDialog(ConstrainedWindow* dlg) {
+ DCHECK(MessageLoop::current() == ui_loop_);
+ dialog_ = dlg;
+ SendNotifications();
+ }
+
+ // Returns the TabContents that needs authentication.
+ TabContents* GetTabContentsForLogin() {
+ DCHECK(MessageLoop::current() == ui_loop_);
+
+ return tab_util::GetTabContentsByID(render_process_host_id_,
+ tab_contents_id_);
+ }
+
+ void set_login_view(LoginView* login_view) {
+ login_view_ = login_view;
+ }
+
+ void set_password_form(const PasswordForm& form) {
+ password_form_ = form;
+ }
+
+ void set_password_manager(PasswordManager* password_manager) {
+ password_manager_ = password_manager;
+ }
+
+ // ChromeViews::DialogDelegate methods:
+ virtual std::wstring GetDialogButtonLabel(DialogButton button) const {
+ if (button == 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(MessageLoop::current() == ui_loop_);
+
+ // Reference is no longer valid.
+ dialog_ = NULL;
+
+ if (!GotAuth()) {
+ request_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &LoginHandlerImpl::CancelAuthDeferred));
+ SendNotifications();
+ }
+
+ // Delete this object once all InvokeLaters have been called.
+ request_loop_->ReleaseSoon(FROM_HERE, this);
+ }
+ virtual bool Cancel() {
+ DCHECK(MessageLoop::current() == ui_loop_);
+ DCHECK(dialog_) << "LoginHandler invoked without being attached";
+ CancelAuth();
+ return true;
+ }
+ virtual bool Accept() {
+ DCHECK(MessageLoop::current() == ui_loop_);
+ DCHECK(dialog_) << "LoginHandler invoked without being attached";
+ SetAuth(login_view_->GetUsername(), login_view_->GetPassword());
+ return true;
+ }
+
+ // LoginHandler:
+ virtual void SetAuth(const std::wstring& username,
+ const std::wstring& password) {
+ if (GotAuth())
+ return;
+
+ // Tell the password manager the credentials were submitted / accepted.
+ if (password_manager_) {
+ password_form_.username_value = username;
+ password_form_.password_value = password;
+ password_manager_->ProvisionallySavePassword(password_form_);
+ }
+
+ ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &LoginHandlerImpl::CloseContentsDeferred));
+ ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &LoginHandlerImpl::SendNotifications));
+ request_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &LoginHandlerImpl::SetAuthDeferred, username, password));
+ }
+
+ virtual void CancelAuth() {
+ if (GotAuth())
+ return;
+
+ ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &LoginHandlerImpl::CloseContentsDeferred));
+ ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &LoginHandlerImpl::SendNotifications));
+ request_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &LoginHandlerImpl::CancelAuthDeferred));
+ }
+
+ virtual void OnRequestCancelled() {
+ DCHECK(MessageLoop::current() == request_loop_) <<
+ "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();
+ }
+
+ private:
+
+ // Calls SetAuth from the request_loop.
+ void SetAuthDeferred(const std::wstring& username,
+ const std::wstring& password) {
+ DCHECK(MessageLoop::current() == request_loop_);
+
+ if (request_) {
+ request_->SetAuth(username, password);
+ ResetLoginHandlerForRequest(request_);
+ }
+ }
+
+ // Calls CancelAuth from the request_loop.
+ void CancelAuthDeferred() {
+ DCHECK(MessageLoop::current() == request_loop_);
+
+ if (request_) {
+ request_->CancelAuth();
+ // Verify that CancelAuth does destroy the request via our delegate.
+ DCHECK(request_ != NULL);
+ ResetLoginHandlerForRequest(request_);
+ }
+ }
+
+ // Closes the view_contents from the UI loop.
+ void CloseContentsDeferred() {
+ DCHECK(MessageLoop::current() == ui_loop_);
+
+ // The hosting ConstrainedWindow may have been freed.
+ if (dialog_)
+ dialog_->CloseConstrainedWindow();
+ }
+
+ // Atomic test-and-set whether we've gotten (or cancelled) authentication.
+ int32 GotAuth() {
+ return base::AtomicSwap(&got_auth_, TRUE);
+ }
+
+ // Notify observers that authentication is needed or received. The automation
+ // proxy uses this for testing.
+ void SendNotifications() {
+ DCHECK(MessageLoop::current() == ui_loop_);
+
+ NotificationService* service = NotificationService::current();
+ TabContents* requesting_contents = GetTabContentsForLogin();
+ if (!requesting_contents)
+ return;
+
+ NavigationController* controller = requesting_contents->controller();
+ if (!got_auth_) {
+ LoginNotificationDetails details(this);
+ service->Notify(NOTIFY_AUTH_NEEDED,
+ Source<NavigationController>(controller),
+ Details<LoginNotificationDetails>(&details));
+ } else {
+ service->Notify(NOTIFY_AUTH_SUPPLIED,
+ Source<NavigationController>(controller),
+ NotificationService::NoDetails());
+ }
+ }
+
+ // Whether SetAuth or CancelAuth have been called.
+ // Must be aligned on a 32-bit boundary.
+ int32 got_auth_;
+
+ // The ConstrainedWindow that is hosting our LoginView.
+ // This should only be accessed on the ui_loop_.
+ ConstrainedWindow* dialog_;
+
+ // The MessageLoop of the thread that the ChromeViewContents lives in.
+ MessageLoop* ui_loop_;
+
+ // The request that wants login data.
+ // This should only be accessed on the request_loop_.
+ URLRequest* request_;
+
+ // The MessageLoop of the thread that the URLRequest lives in.
+ MessageLoop* request_loop_;
+
+ // The LoginView that contains the user's login information
+ LoginView* login_view_;
+
+ // 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_.
+ PasswordForm password_form_;
+
+ // Points to the password manager owned by the TabContents requesting auth.
+ // Can be null if the TabContents is not a WebContents.
+ // This should only be accessed on the ui_loop_.
+ PasswordManager* password_manager_;
+
+ // Cached from the URLRequest, in case it goes NULL on us.
+ int render_process_host_id_;
+ int tab_contents_id_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(LoginHandlerImpl);
+};
+
+
+// ----------------------------------------------------------------------------
+// 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 URLRequest on the I/O thread.
+class LoginDialogTask : public Task {
+ public:
+ LoginDialogTask(AuthChallengeInfo* auth_info, LoginHandlerImpl* handler)
+ : auth_info_(auth_info), handler_(handler) {
+ }
+ virtual ~LoginDialogTask() {
+ }
+
+ void Run() {
+ TabContents* parent_contents = handler_->GetTabContentsForLogin();
+ if (!parent_contents) {
+ // The request was probably cancelled.
+ return;
+ }
+
+ wstring explanation = l10n_util::GetStringF(IDS_LOGIN_DIALOG_DESCRIPTION,
+ auth_info_->host,
+ auth_info_->realm);
+ LoginView* view = new LoginView(explanation);
+ // Tell the password manager to look for saved passwords. There is only
+ // a password manager when dealing with a WebContents type.
+ if (parent_contents->type() == TAB_CONTENTS_WEB) {
+ PasswordManager* password_manager =
+ parent_contents->AsWebContents()->GetPasswordManager();
+ // 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(password_manager);
+ std::vector<PasswordForm> v;
+ MakeInputForPasswordManager(parent_contents->GetURL(), &v);
+ password_manager->PasswordFormsSeen(v);
+ handler_->set_password_manager(password_manager);
+ }
+
+ handler_->set_login_view(view);
+ ConstrainedWindow* dialog =
+ parent_contents->CreateConstrainedDialog(handler_, view);
+ handler_->InitWithDialog(dialog);
+ }
+
+ private:
+ // Helper to create a PasswordForm and stuff it into a vector as input
+ // for PasswordManager::PasswordFormsSeen, the hook into PasswordManager.
+ void MakeInputForPasswordManager(
+ const GURL& origin_url,
+ 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;
+ }
+ dialog_form.origin = origin_url;
+ // TODO(timsteele): Shouldn't depend on HttpKey since a change to the
+ // format would result in not being able to retrieve existing logins
+ // for a site. Refactor HttpKey behavior to be more reusable.
+ dialog_form.signon_realm = AuthCache::HttpKey(dialog_form.origin,
+ *auth_info_);
+ password_manager_input->push_back(dialog_form);
+ // Set the password form for the handler (by copy).
+ handler_->set_password_form(dialog_form);
+ }
+
+ // Info about who/where/what is asking for authentication.
+ scoped_refptr<AuthChallengeInfo> auth_info_;
+
+ // Where to send the authentication when obtained.
+ // This is owned by the ResourceDispatcherHost that invoked us.
+ LoginHandlerImpl* handler_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(LoginDialogTask);
+};
+
+// ----------------------------------------------------------------------------
+// Public API
+
+LoginHandler* CreateLoginPrompt(AuthChallengeInfo* auth_info,
+ URLRequest* request,
+ MessageLoop* ui_loop) {
+ LoginHandlerImpl* handler = new LoginHandlerImpl(request, ui_loop);
+ ui_loop->PostTask(FROM_HERE, new LoginDialogTask(auth_info, handler));
+ return handler;
+}