Home | History | Annotate | Download | only in gtk
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/ui/login/login_prompt.h"
      6 
      7 #include <gtk/gtk.h>
      8 
      9 #include "base/strings/string16.h"
     10 #include "base/strings/utf_string_conversions.h"
     11 #include "chrome/browser/password_manager/password_manager.h"
     12 #include "chrome/browser/tab_contents/tab_util.h"
     13 #include "chrome/browser/ui/gtk/constrained_window_gtk.h"
     14 #include "chrome/browser/ui/gtk/gtk_util.h"
     15 #include "chrome/browser/ui/login/login_model.h"
     16 #include "components/web_modal/web_contents_modal_dialog_manager.h"
     17 #include "content/public/browser/browser_thread.h"
     18 #include "content/public/browser/web_contents.h"
     19 #include "content/public/browser/web_contents_delegate.h"
     20 #include "grit/generated_resources.h"
     21 #include "net/url_request/url_request.h"
     22 #include "ui/base/gtk/gtk_compat.h"
     23 #include "ui/base/gtk/gtk_hig_constants.h"
     24 #include "ui/base/gtk/gtk_signal.h"
     25 #include "ui/base/gtk/scoped_gobject.h"
     26 #include "ui/base/l10n/l10n_util.h"
     27 
     28 using content::BrowserThread;
     29 using content::PasswordForm;
     30 using content::WebContents;
     31 using web_modal::WebContentsModalDialogManager;
     32 
     33 // ----------------------------------------------------------------------------
     34 // LoginHandlerGtk
     35 
     36 // This class simply forwards the authentication from the LoginView (on
     37 // the UI thread) to the net::URLRequest (on the I/O thread).
     38 // This class uses ref counting to ensure that it lives until all InvokeLaters
     39 // have been called.
     40 class LoginHandlerGtk : public LoginHandler {
     41  public:
     42   LoginHandlerGtk(net::AuthChallengeInfo* auth_info, net::URLRequest* request)
     43       : LoginHandler(auth_info, request),
     44         username_entry_(NULL),
     45         password_entry_(NULL),
     46         ok_(NULL),
     47         dialog_(NULL) {
     48   }
     49 
     50   // LoginModelObserver implementation.
     51   virtual void OnAutofillDataAvailable(const string16& username,
     52                                        const string16& password) OVERRIDE {
     53     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     54 
     55     // NOTE: Would be nice to use gtk_entry_get_text_length, but it is fairly
     56     // new and not always in our GTK version.
     57     if (strlen(gtk_entry_get_text(GTK_ENTRY(username_entry_))) == 0) {
     58       gtk_entry_set_text(GTK_ENTRY(username_entry_),
     59                          UTF16ToUTF8(username).c_str());
     60       gtk_entry_set_text(GTK_ENTRY(password_entry_),
     61                          UTF16ToUTF8(password).c_str());
     62       gtk_editable_select_region(GTK_EDITABLE(username_entry_), 0, -1);
     63     }
     64   }
     65   virtual void OnLoginModelDestroying() OVERRIDE {}
     66 
     67   // LoginHandler:
     68   virtual void BuildViewForPasswordManager(
     69       PasswordManager* manager,
     70       const string16& explanation) OVERRIDE {
     71     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     72 
     73     root_.reset(gtk_vbox_new(FALSE, ui::kContentAreaBorder));
     74     g_object_ref_sink(root_.get());
     75     g_signal_connect(root_.get(), "destroy", G_CALLBACK(OnDestroyThunk), this);
     76 
     77     GtkWidget* label = gtk_label_new(UTF16ToUTF8(explanation).c_str());
     78     gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
     79     gtk_box_pack_start(GTK_BOX(root_.get()), label, FALSE, FALSE, 0);
     80 
     81     username_entry_ = gtk_entry_new();
     82     gtk_entry_set_activates_default(GTK_ENTRY(username_entry_), TRUE);
     83 
     84     password_entry_ = gtk_entry_new();
     85     gtk_entry_set_activates_default(GTK_ENTRY(password_entry_), TRUE);
     86     gtk_entry_set_visibility(GTK_ENTRY(password_entry_), FALSE);
     87 
     88     GtkWidget* table = gtk_util::CreateLabeledControlsGroup(NULL,
     89         l10n_util::GetStringUTF8(IDS_LOGIN_DIALOG_USERNAME_FIELD).c_str(),
     90         username_entry_,
     91         l10n_util::GetStringUTF8(IDS_LOGIN_DIALOG_PASSWORD_FIELD).c_str(),
     92         password_entry_,
     93         NULL);
     94     gtk_box_pack_start(GTK_BOX(root_.get()), table, FALSE, FALSE, 0);
     95 
     96     GtkWidget* hbox = gtk_hbox_new(FALSE, 12);
     97     gtk_box_pack_start(GTK_BOX(root_.get()), hbox, FALSE, FALSE, 0);
     98 
     99     ok_ = gtk_button_new_from_stock(GTK_STOCK_OK);
    100     gtk_button_set_label(
    101         GTK_BUTTON(ok_),
    102         l10n_util::GetStringUTF8(IDS_LOGIN_DIALOG_OK_BUTTON_LABEL).c_str());
    103     g_signal_connect(ok_, "clicked", G_CALLBACK(OnOKClickedThunk), this);
    104     gtk_box_pack_end(GTK_BOX(hbox), ok_, FALSE, FALSE, 0);
    105 
    106     GtkWidget* cancel = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
    107     g_signal_connect(cancel, "clicked", G_CALLBACK(OnCancelClickedThunk), this);
    108     gtk_box_pack_end(GTK_BOX(hbox), cancel, FALSE, FALSE, 0);
    109 
    110     g_signal_connect(root_.get(), "hierarchy-changed",
    111                      G_CALLBACK(OnPromptHierarchyChangedThunk), this);
    112 
    113     SetModel(manager);
    114 
    115     // Scary thread safety note: This can potentially be called *after* SetAuth
    116     // or CancelAuth (say, if the request was cancelled before the UI thread got
    117     // control).  However, that's OK since any UI interaction in those functions
    118     // will occur via an InvokeLater on the UI thread, which is guaranteed
    119     // to happen after this is called (since this was InvokeLater'd first).
    120     WebContents* requesting_contents = GetWebContentsForLogin();
    121     DCHECK(requesting_contents);
    122 
    123     dialog_ = CreateWebContentsModalDialogGtk(root_.get(), username_entry_);
    124 
    125     WebContentsModalDialogManager* web_contents_modal_dialog_manager =
    126         WebContentsModalDialogManager::FromWebContents(requesting_contents);
    127     web_contents_modal_dialog_manager->ShowDialog(dialog_);
    128 
    129     NotifyAuthNeeded();
    130   }
    131 
    132   virtual void CloseDialog() OVERRIDE {
    133     // The hosting dialog may have been freed.
    134     if (dialog_)
    135       gtk_widget_destroy(dialog_);
    136   }
    137 
    138  protected:
    139   virtual ~LoginHandlerGtk() {
    140   }
    141 
    142  private:
    143   friend class LoginPrompt;
    144 
    145   CHROMEGTK_CALLBACK_0(LoginHandlerGtk, void, OnOKClicked);
    146   CHROMEGTK_CALLBACK_0(LoginHandlerGtk, void, OnCancelClicked);
    147   CHROMEGTK_CALLBACK_1(LoginHandlerGtk, void, OnPromptHierarchyChanged,
    148                        GtkWidget*);
    149   CHROMEGTK_CALLBACK_0(LoginHandlerGtk, void, OnDestroy);
    150 
    151   // The GtkWidgets that form our visual hierarchy:
    152   // The root container we pass to our parent.
    153   ui::ScopedGObject<GtkWidget>::Type root_;
    154 
    155   // GtkEntry widgets that the user types into.
    156   GtkWidget* username_entry_;
    157   GtkWidget* password_entry_;
    158   GtkWidget* ok_;
    159 
    160   GtkWidget* dialog_;
    161 
    162   DISALLOW_COPY_AND_ASSIGN(LoginHandlerGtk);
    163 };
    164 
    165 void LoginHandlerGtk::OnOKClicked(GtkWidget* sender) {
    166   SetAuth(
    167       UTF8ToUTF16(gtk_entry_get_text(GTK_ENTRY(username_entry_))),
    168       UTF8ToUTF16(gtk_entry_get_text(GTK_ENTRY(password_entry_))));
    169 }
    170 
    171 void LoginHandlerGtk::OnCancelClicked(GtkWidget* sender) {
    172   CancelAuth();
    173 }
    174 
    175 void LoginHandlerGtk::OnPromptHierarchyChanged(GtkWidget* sender,
    176                                                GtkWidget* previous_toplevel) {
    177   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    178 
    179   if (!gtk_widget_is_toplevel(gtk_widget_get_toplevel(ok_)))
    180     return;
    181 
    182   // Now that we have attached ourself to the window, we can make our OK
    183   // button the default action and mess with the focus.
    184   gtk_widget_set_can_default(ok_, TRUE);
    185   gtk_widget_grab_default(ok_);
    186 }
    187 
    188 // static
    189 LoginHandler* LoginHandler::Create(net::AuthChallengeInfo* auth_info,
    190                                    net::URLRequest* request) {
    191   return new LoginHandlerGtk(auth_info, request);
    192 }
    193 
    194 void LoginHandlerGtk::OnDestroy(GtkWidget* widget) {
    195   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    196 
    197   // The web contents modal dialog is going to delete itself; clear our pointer.
    198   dialog_ = NULL;
    199   SetModel(NULL);
    200 
    201   ReleaseSoon();
    202 }
    203