Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(459)

Unified Diff: chrome/browser/login_prompt_gtk.cc

Issue 132047: GTK: HTTP Auth dialogs under linux. (Closed)
Patch Set: Fix for evanm Created 11 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/login_prompt_gtk.cc
diff --git a/chrome/browser/login_prompt_gtk.cc b/chrome/browser/login_prompt_gtk.cc
new file mode 100644
index 0000000000000000000000000000000000000000..11344e02db57772dac9f7766b857f8cd43629513
--- /dev/null
+++ b/chrome/browser/login_prompt_gtk.cc
@@ -0,0 +1,317 @@
+// 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();
+ }
+ }
+
+ ~LoginHandlerGtk() {
Evan Martin 2009/06/19 21:55:48 virtual
+ 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);
+
+ 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);
+
+ cancel_ = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
+ gtk_box_pack_end(GTK_BOX(hbox), cancel_, FALSE, FALSE, 0);
+ g_signal_connect(cancel_, "clicked", G_CALLBACK(OnCancelClicked), this);
+
+ // 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?";
Evan Martin 2009/06/19 21:55:48 I guess this text is a bit redundant with the DCHE
+
+ // 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() {
Evan Martin 2009/06/19 21:55:48 It's a bit weird that all of these are inside the
+ 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_;
+
+ // The action buttons on the bottom.
+ GtkWidget* ok_;
+ GtkWidget* cancel_;
Evan Martin 2009/06/19 21:55:48 These don't need to be members, it seems -- they'r
+
+ DISALLOW_COPY_AND_ASSIGN(LoginHandlerGtk);
+};
+
+// static
+LoginHandler* LoginHandler::Create(URLRequest* request, MessageLoop* ui_loop) {
+ return new LoginHandlerGtk(request, ui_loop);
+}

Powered by Google App Engine
This is Rietveld 408576698