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.
5 #include "chrome/browser/ui/login/login_prompt.h"
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"
28 using autofill::PasswordForm
;
29 using content::BrowserThread
;
30 using content::WebContents
;
31 using web_modal::WebContentsModalDialogManager
;
33 // ----------------------------------------------------------------------------
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
40 class LoginHandlerGtk
: public LoginHandler
{
42 LoginHandlerGtk(net::AuthChallengeInfo
* auth_info
, net::URLRequest
* request
)
43 : LoginHandler(auth_info
, request
),
44 username_entry_(NULL
),
45 password_entry_(NULL
),
50 // LoginModelObserver implementation.
51 virtual void OnAutofillDataAvailable(
52 const base::string16
& username
,
53 const base::string16
& password
) OVERRIDE
{
54 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
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 base::UTF16ToUTF8(username
).c_str());
61 gtk_entry_set_text(GTK_ENTRY(password_entry_
),
62 base::UTF16ToUTF8(password
).c_str());
63 gtk_editable_select_region(GTK_EDITABLE(username_entry_
), 0, -1);
66 virtual void OnLoginModelDestroying() OVERRIDE
{}
69 virtual void BuildViewForPasswordManager(
70 PasswordManager
* manager
,
71 const base::string16
& explanation
) OVERRIDE
{
72 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
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);
78 GtkWidget
* label
= gtk_label_new(base::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);
82 username_entry_
= gtk_entry_new();
83 gtk_entry_set_activates_default(GTK_ENTRY(username_entry_
), TRUE
);
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
);
89 GtkWidget
* table
= gtk_util::CreateLabeledControlsGroup(NULL
,
90 l10n_util::GetStringUTF8(IDS_LOGIN_DIALOG_USERNAME_FIELD
).c_str(),
92 l10n_util::GetStringUTF8(IDS_LOGIN_DIALOG_PASSWORD_FIELD
).c_str(),
95 gtk_box_pack_start(GTK_BOX(root_
.get()), table
, FALSE
, FALSE
, 0);
97 GtkWidget
* hbox
= gtk_hbox_new(FALSE
, 12);
98 gtk_box_pack_start(GTK_BOX(root_
.get()), hbox
, FALSE
, FALSE
, 0);
100 ok_
= gtk_button_new_from_stock(GTK_STOCK_OK
);
101 gtk_button_set_label(
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);
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);
111 g_signal_connect(root_
.get(), "hierarchy-changed",
112 G_CALLBACK(OnPromptHierarchyChangedThunk
), this);
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
);
124 dialog_
= CreateWebContentsModalDialogGtk(root_
.get(), username_entry_
);
126 WebContentsModalDialogManager
* web_contents_modal_dialog_manager
=
127 WebContentsModalDialogManager::FromWebContents(requesting_contents
);
128 web_contents_modal_dialog_manager
->ShowDialog(dialog_
);
133 virtual void CloseDialog() OVERRIDE
{
134 // The hosting dialog may have been freed.
136 gtk_widget_destroy(dialog_
);
140 virtual ~LoginHandlerGtk() {
144 friend class LoginPrompt
;
146 CHROMEGTK_CALLBACK_0(LoginHandlerGtk
, void, OnOKClicked
);
147 CHROMEGTK_CALLBACK_0(LoginHandlerGtk
, void, OnCancelClicked
);
148 CHROMEGTK_CALLBACK_1(LoginHandlerGtk
, void, OnPromptHierarchyChanged
,
150 CHROMEGTK_CALLBACK_0(LoginHandlerGtk
, void, OnDestroy
);
152 // The GtkWidgets that form our visual hierarchy:
153 // The root container we pass to our parent.
154 ui::ScopedGObject
<GtkWidget
>::Type root_
;
156 // GtkEntry widgets that the user types into.
157 GtkWidget
* username_entry_
;
158 GtkWidget
* password_entry_
;
163 DISALLOW_COPY_AND_ASSIGN(LoginHandlerGtk
);
166 void LoginHandlerGtk::OnOKClicked(GtkWidget
* sender
) {
168 base::UTF8ToUTF16(gtk_entry_get_text(GTK_ENTRY(username_entry_
))),
169 base::UTF8ToUTF16(gtk_entry_get_text(GTK_ENTRY(password_entry_
))));
172 void LoginHandlerGtk::OnCancelClicked(GtkWidget
* sender
) {
176 void LoginHandlerGtk::OnPromptHierarchyChanged(GtkWidget
* sender
,
177 GtkWidget
* previous_toplevel
) {
178 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
180 if (!gtk_widget_is_toplevel(gtk_widget_get_toplevel(ok_
)))
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_
);
190 LoginHandler
* LoginHandler::Create(net::AuthChallengeInfo
* auth_info
,
191 net::URLRequest
* request
) {
192 return new LoginHandlerGtk(auth_info
, request
);
195 void LoginHandlerGtk::OnDestroy(GtkWidget
* widget
) {
196 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
198 // The web contents modal dialog is going to delete itself; clear our pointer.