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_hig_constants.h"
     23 #include "ui/base/gtk/gtk_signal.h"
     24 #include "ui/base/l10n/l10n_util.h"
     25 #include "ui/gfx/gtk_compat.h"
     26 #include "ui/gfx/scoped_gobject.h"
     27 
     28 using autofill::PasswordForm;
     29 using content::BrowserThread;
     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(
     52       const base::string16& username,
     53       const base::string16& password) OVERRIDE {
     54     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     55 
     56     // NOTE: Would be nice to use gtk_entry_get_text_length, but it is fairly
     57     // new and not always in our GTK version.
     58     if (strlen(gtk_entry_get_text(GTK_ENTRY(username_entry_))) == 0) {
     59       gtk_entry_set_text(GTK_ENTRY(username_entry_),
     60                          UTF16ToUTF8(username).c_str());
     61       gtk_entry_set_text(GTK_ENTRY(password_entry_),
     62                          UTF16ToUTF8(password).c_str());
     63       gtk_editable_select_region(GTK_EDITABLE(username_entry_), 0, -1);
     64     }
     65   }
     66   virtual void OnLoginModelDestroying() OVERRIDE {}
     67 
     68   // LoginHandler:
     69   virtual void BuildViewForPasswordManager(
     70       PasswordManager* manager,
     71       const base::string16& explanation) OVERRIDE {
     72     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     73 
     74     root_.reset(gtk_vbox_new(FALSE, ui::kContentAreaBorder));
     75     g_object_ref_sink(root_.get());
     76     g_signal_connect(root_.get(), "destroy", G_CALLBACK(OnDestroyThunk), this);
     77 
     78     GtkWidget* label = gtk_label_new(UTF16ToUTF8(explanation).c_str());
     79     gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
     80     gtk_box_pack_start(GTK_BOX(root_.get()), label, FALSE, FALSE, 0);
     81 
     82     username_entry_ = gtk_entry_new();
     83     gtk_entry_set_activates_default(GTK_ENTRY(username_entry_), TRUE);
     84 
     85     password_entry_ = gtk_entry_new();
     86     gtk_entry_set_activates_default(GTK_ENTRY(password_entry_), TRUE);
     87     gtk_entry_set_visibility(GTK_ENTRY(password_entry_), FALSE);
     88 
     89     GtkWidget* table = gtk_util::CreateLabeledControlsGroup(NULL,
     90         l10n_util::GetStringUTF8(IDS_LOGIN_DIALOG_USERNAME_FIELD).c_str(),
     91         username_entry_,
     92         l10n_util::GetStringUTF8(IDS_LOGIN_DIALOG_PASSWORD_FIELD).c_str(),
     93         password_entry_,
     94         NULL);
     95     gtk_box_pack_start(GTK_BOX(root_.get()), table, FALSE, FALSE, 0);
     96 
     97     GtkWidget* hbox = gtk_hbox_new(FALSE, 12);
     98     gtk_box_pack_start(GTK_BOX(root_.get()), hbox, FALSE, FALSE, 0);
     99 
    100     ok_ = gtk_button_new_from_stock(GTK_STOCK_OK);
    101     gtk_button_set_label(
    102         GTK_BUTTON(ok_),
    103         l10n_util::GetStringUTF8(IDS_LOGIN_DIALOG_OK_BUTTON_LABEL).c_str());
    104     g_signal_connect(ok_, "clicked", G_CALLBACK(OnOKClickedThunk), this);
    105     gtk_box_pack_end(GTK_BOX(hbox), ok_, FALSE, FALSE, 0);
    106 
    107     GtkWidget* cancel = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
    108     g_signal_connect(cancel, "clicked", G_CALLBACK(OnCancelClickedThunk), this);
    109     gtk_box_pack_end(GTK_BOX(hbox), cancel, FALSE, FALSE, 0);
    110 
    111     g_signal_connect(root_.get(), "hierarchy-changed",
    112                      G_CALLBACK(OnPromptHierarchyChangedThunk), this);
    113 
    114     SetModel(manager);
    115 
    116     // Scary thread safety note: This can potentially be called *after* SetAuth
    117     // or CancelAuth (say, if the request was cancelled before the UI thread got
    118     // control).  However, that's OK since any UI interaction in those functions
    119     // will occur via an InvokeLater on the UI thread, which is guaranteed
    120     // to happen after this is called (since this was InvokeLater'd first).
    121     WebContents* requesting_contents = GetWebContentsForLogin();
    122     DCHECK(requesting_contents);
    123 
    124     dialog_ = CreateWebContentsModalDialogGtk(root_.get(), username_entry_);
    125 
    126     WebContentsModalDialogManager* web_contents_modal_dialog_manager =
    127         WebContentsModalDialogManager::FromWebContents(requesting_contents);
    128     web_contents_modal_dialog_manager->ShowDialog(dialog_);
    129 
    130     NotifyAuthNeeded();
    131   }
    132 
    133   virtual void CloseDialog() OVERRIDE {
    134     // The hosting dialog may have been freed.
    135     if (dialog_)
    136       gtk_widget_destroy(dialog_);
    137   }
    138 
    139  protected:
    140   virtual ~LoginHandlerGtk() {
    141   }
    142 
    143  private:
    144   friend class LoginPrompt;
    145 
    146   CHROMEGTK_CALLBACK_0(LoginHandlerGtk, void, OnOKClicked);
    147   CHROMEGTK_CALLBACK_0(LoginHandlerGtk, void, OnCancelClicked);
    148   CHROMEGTK_CALLBACK_1(LoginHandlerGtk, void, OnPromptHierarchyChanged,
    149                        GtkWidget*);
    150   CHROMEGTK_CALLBACK_0(LoginHandlerGtk, void, OnDestroy);
    151 
    152   // The GtkWidgets that form our visual hierarchy:
    153   // The root container we pass to our parent.
    154   ui::ScopedGObject<GtkWidget>::Type root_;
    155 
    156   // GtkEntry widgets that the user types into.
    157   GtkWidget* username_entry_;
    158   GtkWidget* password_entry_;
    159   GtkWidget* ok_;
    160 
    161   GtkWidget* dialog_;
    162 
    163   DISALLOW_COPY_AND_ASSIGN(LoginHandlerGtk);
    164 };
    165 
    166 void LoginHandlerGtk::OnOKClicked(GtkWidget* sender) {
    167   SetAuth(
    168       UTF8ToUTF16(gtk_entry_get_text(GTK_ENTRY(username_entry_))),
    169       UTF8ToUTF16(gtk_entry_get_text(GTK_ENTRY(password_entry_))));
    170 }
    171 
    172 void LoginHandlerGtk::OnCancelClicked(GtkWidget* sender) {
    173   CancelAuth();
    174 }
    175 
    176 void LoginHandlerGtk::OnPromptHierarchyChanged(GtkWidget* sender,
    177                                                GtkWidget* previous_toplevel) {
    178   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    179 
    180   if (!gtk_widget_is_toplevel(gtk_widget_get_toplevel(ok_)))
    181     return;
    182 
    183   // Now that we have attached ourself to the window, we can make our OK
    184   // button the default action and mess with the focus.
    185   gtk_widget_set_can_default(ok_, TRUE);
    186   gtk_widget_grab_default(ok_);
    187 }
    188 
    189 // static
    190 LoginHandler* LoginHandler::Create(net::AuthChallengeInfo* auth_info,
    191                                    net::URLRequest* request) {
    192   return new LoginHandlerGtk(auth_info, request);
    193 }
    194 
    195 void LoginHandlerGtk::OnDestroy(GtkWidget* widget) {
    196   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    197 
    198   // The web contents modal dialog is going to delete itself; clear our pointer.
    199   dialog_ = NULL;
    200   SetModel(NULL);
    201 
    202   ReleaseSoon();
    203 }
    204