diff options
author | erg@google.com <erg@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-19 22:16:28 +0000 |
---|---|---|
committer | erg@google.com <erg@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-19 22:16:28 +0000 |
commit | e838217a07f277f8bf751aaaac6fb1005a533d47 (patch) | |
tree | f34f7f4522a9eed3a8172751fb1a3202705c16f6 /chrome/browser/login_prompt_gtk.cc | |
parent | e804e052523e23df3881132ab2e31791e0c0d1b6 (diff) | |
download | chromium_src-e838217a07f277f8bf751aaaac6fb1005a533d47.zip chromium_src-e838217a07f277f8bf751aaaac6fb1005a533d47.tar.gz chromium_src-e838217a07f277f8bf751aaaac6fb1005a533d47.tar.bz2 |
GTK: HTTP Auth dialogs under linux.
- Implements a ConstrainedWindowGtk which positions itself in the center of its corresponding TabContentsViewGtk.
- Implements LoginPromptGtk. HTTP Auth now works under Linux.
- Renames ConstrainedWindowImpl to ConstrainedWindowWin
http://crbug.com/11512
Review URL: http://codereview.chromium.org/132047
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@18864 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/login_prompt_gtk.cc')
-rw-r--r-- | chrome/browser/login_prompt_gtk.cc | 312 |
1 files changed, 312 insertions, 0 deletions
diff --git a/chrome/browser/login_prompt_gtk.cc b/chrome/browser/login_prompt_gtk.cc new file mode 100644 index 0000000..f52f2ee --- /dev/null +++ b/chrome/browser/login_prompt_gtk.cc @@ -0,0 +1,312 @@ +// 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/login_prompt.h" + +#include <gtk/gtk.h> + +#include "app/l10n_util.h" +#include "base/message_loop.h" +#include "chrome/browser/gtk/constrained_window_gtk.h" +#include "chrome/browser/password_manager/password_manager.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/common/gtk_util.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 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 base::RefCountedThreadSafe<LoginHandlerGtk>, + public ConstrainedWindowGtkDelegate { + public: + LoginHandlerGtk(URLRequest* request, MessageLoop* ui_loop) + : handled_auth_(false), + dialog_(NULL), + ui_loop_(ui_loop), + request_(request), + request_loop_(MessageLoop::current()), + password_manager_(NULL) { + DCHECK(request_) << "LoginHandlerGtk constructed with NULL request"; + + AddRef(); // matched by ReleaseLater. + if (!tab_util::GetTabContentsID(request_, &render_process_host_id_, + &tab_contents_id_)) { + NOTREACHED(); + } + } + + virtual ~LoginHandlerGtk() { + root_.Destroy(); + } + + // LoginHandler: + virtual void BuildViewForPasswordManager(PasswordManager* manager, + std::wstring explanation) { + DCHECK(MessageLoop::current() == ui_loop_); + + root_.Own(gtk_vbox_new(NULL, gtk_util::kContentAreaBorder)); + gtk_box_pack_start(GTK_BOX(root_.get()), + gtk_label_new(WideToUTF8(explanation).c_str()), + 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( + 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); + + GtkWidget* 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(OnOKClicked), 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(OnCancelClicked), this); + gtk_box_pack_end(GTK_BOX(hbox), cancel, FALSE, FALSE, 0); + + // 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). + dialog_ = GetTabContentsForLogin()->CreateConstrainedDialog(this); + + // 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); + gtk_widget_grab_focus(username_entry_); + + SendNotifications(); + } + + virtual void SetPasswordForm(const webkit_glue::PasswordForm& form) { + password_form_ = form; + } + + virtual void SetPasswordManager(PasswordManager* password_manager) { + password_manager_ = password_manager; + } + + virtual TabContents* GetTabContentsForLogin() { + DCHECK(MessageLoop::current() == ui_loop_); + + return tab_util::GetTabContentsByID(render_process_host_id_, + tab_contents_id_); + } + + virtual void 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 = username; + password_form_.password_value = password; + password_manager_->ProvisionallySavePassword(password_form_); + } + + ui_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &LoginHandlerGtk::CloseContentsDeferred)); + ui_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &LoginHandlerGtk::SendNotifications)); + request_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &LoginHandlerGtk::SetAuthDeferred, username, password)); + } + + virtual void CancelAuth() { + if (WasAuthHandled(true)) + return; + + ui_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &LoginHandlerGtk::CloseContentsDeferred)); + ui_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &LoginHandlerGtk::SendNotifications)); + request_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &LoginHandlerGtk::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(); + } + + // Overridden from ConstrainedWindowGtkDelegate: + virtual GtkWidget* GetWidgetRoot() { + return root_.get(); + } + + virtual void DeleteDelegate() { + if (!WasAuthHandled(true)) { + request_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &LoginHandlerGtk::CancelAuthDeferred)); + SendNotifications(); + } + + // Delete this object once all InvokeLaters have been called. + request_loop_->ReleaseSoon(FROM_HERE, this); + } + + private: + friend class LoginPrompt; + + // 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(); + } + + // 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) { + AutoLock lock(handled_auth_lock_); + bool was_handled = handled_auth_; + if (set_handled) + handled_auth_ = true; + return was_handled; + } + + // 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 (!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()); + } + } + + static void OnOKClicked(GtkButton *button, LoginHandlerGtk* handler) { + DCHECK(MessageLoop::current() == handler->ui_loop_); + + handler->SetAuth( + UTF8ToWide(gtk_entry_get_text(GTK_ENTRY(handler->username_entry_))), + UTF8ToWide(gtk_entry_get_text(GTK_ENTRY(handler->password_entry_)))); + } + + static void OnCancelClicked(GtkButton *button, LoginHandlerGtk* handler) { + DCHECK(MessageLoop::current() == handler->ui_loop_); + handler->CancelAuth(); + } + + // 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_; + + // 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 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 TabContents. + // 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_; + + // 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_; + + DISALLOW_COPY_AND_ASSIGN(LoginHandlerGtk); +}; + +// static +LoginHandler* LoginHandler::Create(URLRequest* request, MessageLoop* ui_loop) { + return new LoginHandlerGtk(request, ui_loop); +} |