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

Side by Side 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2009 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/login_prompt.h"
6
7 #include <gtk/gtk.h>
8
9 #include "app/l10n_util.h"
10 #include "base/message_loop.h"
11 #include "chrome/browser/gtk/constrained_window_gtk.h"
12 #include "chrome/browser/password_manager/password_manager.h"
13 #include "chrome/browser/tab_contents/navigation_controller.h"
14 #include "chrome/browser/tab_contents/tab_contents.h"
15 #include "chrome/browser/tab_contents/tab_util.h"
16 #include "chrome/common/gtk_util.h"
17 #include "chrome/common/notification_service.h"
18 #include "grit/generated_resources.h"
19 #include "net/url_request/url_request.h"
20
21 using webkit_glue::PasswordForm;
22
23 // ----------------------------------------------------------------------------
24 // LoginHandlerGtk
25
26 // This class simply forwards the authentication from the LoginView (on
27 // the UI thread) to the URLRequest (on the I/O thread).
28 // This class uses ref counting to ensure that it lives until all InvokeLaters
29 // have been called.
30 class LoginHandlerGtk : public LoginHandler,
31 public base::RefCountedThreadSafe<LoginHandlerGtk>,
32 public ConstrainedWindowGtkDelegate {
33 public:
34 LoginHandlerGtk(URLRequest* request, MessageLoop* ui_loop)
35 : handled_auth_(false),
36 dialog_(NULL),
37 ui_loop_(ui_loop),
38 request_(request),
39 request_loop_(MessageLoop::current()),
40 password_manager_(NULL) {
41 DCHECK(request_) << "LoginHandlerGtk constructed with NULL request";
42
43 AddRef(); // matched by ReleaseLater.
44 if (!tab_util::GetTabContentsID(request_, &render_process_host_id_,
45 &tab_contents_id_)) {
46 NOTREACHED();
47 }
48 }
49
50 ~LoginHandlerGtk() {
Evan Martin 2009/06/19 21:55:48 virtual
51 root_.Destroy();
52 }
53
54 // LoginHandler:
55 virtual void BuildViewForPasswordManager(PasswordManager* manager,
56 std::wstring explanation) {
57 DCHECK(MessageLoop::current() == ui_loop_);
58
59 root_.Own(gtk_vbox_new(NULL, gtk_util::kContentAreaBorder));
60 gtk_box_pack_start(GTK_BOX(root_.get()),
61 gtk_label_new(WideToUTF8(explanation).c_str()),
62 FALSE, FALSE, 0);
63
64 username_entry_ = gtk_entry_new();
65 gtk_entry_set_activates_default(GTK_ENTRY(username_entry_), TRUE);
66
67 password_entry_ = gtk_entry_new();
68 gtk_entry_set_activates_default(GTK_ENTRY(password_entry_), TRUE);
69 gtk_entry_set_visibility(GTK_ENTRY(password_entry_), FALSE);
70
71 GtkWidget* table = gtk_util::CreateLabeledControlsGroup(
72 l10n_util::GetStringUTF8(IDS_LOGIN_DIALOG_USERNAME_FIELD).c_str(),
73 username_entry_,
74 l10n_util::GetStringUTF8(IDS_LOGIN_DIALOG_PASSWORD_FIELD).c_str(),
75 password_entry_,
76 NULL);
77 gtk_box_pack_start(GTK_BOX(root_.get()), table, FALSE, FALSE, 0);
78
79 GtkWidget* hbox = gtk_hbox_new(FALSE, 12);
80 gtk_box_pack_start(GTK_BOX(root_.get()), hbox, FALSE, FALSE, 0);
81
82 ok_ = gtk_button_new_from_stock(GTK_STOCK_OK);
83 gtk_button_set_label(
84 GTK_BUTTON(ok_),
85 l10n_util::GetStringUTF8(IDS_LOGIN_DIALOG_OK_BUTTON_LABEL).c_str());
86 g_signal_connect(ok_, "clicked", G_CALLBACK(OnOKClicked), this);
87
88 gtk_box_pack_end(GTK_BOX(hbox), ok_, FALSE, FALSE, 0);
89
90 cancel_ = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
91 gtk_box_pack_end(GTK_BOX(hbox), cancel_, FALSE, FALSE, 0);
92 g_signal_connect(cancel_, "clicked", G_CALLBACK(OnCancelClicked), this);
93
94 // Scary thread safety note: This can potentially be called *after* SetAuth
95 // or CancelAuth (say, if the request was cancelled before the UI thread got
96 // control). However, that's OK since any UI interaction in those functions
97 // will occur via an InvokeLater on the UI thread, which is guaranteed
98 // to happen after this is called (since this was InvokeLater'd first).
99 dialog_ = GetTabContentsForLogin()->CreateConstrainedDialog(this);
100
101 // Now that we have attached ourself to the window, we can make our OK
102 // button the default action and mess with the focus.
103 GTK_WIDGET_SET_FLAGS(ok_, GTK_CAN_DEFAULT);
104 gtk_widget_grab_default(ok_);
105 gtk_widget_grab_focus(username_entry_);
106
107 SendNotifications();
108 }
109
110 virtual void SetPasswordForm(const webkit_glue::PasswordForm& form) {
111 password_form_ = form;
112 }
113
114 virtual void SetPasswordManager(PasswordManager* password_manager) {
115 password_manager_ = password_manager;
116 }
117
118 virtual TabContents* GetTabContentsForLogin() {
119 DCHECK(MessageLoop::current() == ui_loop_);
120
121 return tab_util::GetTabContentsByID(render_process_host_id_,
122 tab_contents_id_);
123 }
124
125 virtual void SetAuth(const std::wstring& username,
126 const std::wstring& password) {
127 if (WasAuthHandled(true))
128 return;
129
130 // Tell the password manager the credentials were submitted / accepted.
131 if (password_manager_) {
132 password_form_.username_value = username;
133 password_form_.password_value = password;
134 password_manager_->ProvisionallySavePassword(password_form_);
135 }
136
137 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(
138 this, &LoginHandlerGtk::CloseContentsDeferred));
139 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(
140 this, &LoginHandlerGtk::SendNotifications));
141 request_loop_->PostTask(FROM_HERE, NewRunnableMethod(
142 this, &LoginHandlerGtk::SetAuthDeferred, username, password));
143 }
144
145 virtual void CancelAuth() {
146 if (WasAuthHandled(true))
147 return;
148
149 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(
150 this, &LoginHandlerGtk::CloseContentsDeferred));
151 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(
152 this, &LoginHandlerGtk::SendNotifications));
153 request_loop_->PostTask(FROM_HERE, NewRunnableMethod(
154 this, &LoginHandlerGtk::CancelAuthDeferred));
155 }
156
157 virtual void OnRequestCancelled() {
158 DCHECK(MessageLoop::current() == request_loop_) <<
159 "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
160
161 // Reference is no longer valid.
162 request_ = NULL;
163
164 // Give up on auth if the request was cancelled.
165 CancelAuth();
166 }
167
168 // Overridden from ConstrainedWindowGtkDelegate:
169 virtual GtkWidget* GetWidgetRoot() {
170 return root_.get();
171 }
172
173 virtual void DeleteDelegate() {
174 if (!WasAuthHandled(true)) {
175 request_loop_->PostTask(FROM_HERE, NewRunnableMethod(
176 this, &LoginHandlerGtk::CancelAuthDeferred));
177 SendNotifications();
178 }
179
180 // Delete this object once all InvokeLaters have been called.
181 request_loop_->ReleaseSoon(FROM_HERE, this);
182 }
183
184 private:
185 friend class LoginPrompt;
186
187 // Calls SetAuth from the request_loop.
188 void SetAuthDeferred(const std::wstring& username,
189 const std::wstring& password) {
190 DCHECK(MessageLoop::current() == request_loop_);
191
192 if (request_) {
193 request_->SetAuth(username, password);
194 ResetLoginHandlerForRequest(request_);
195 }
196 }
197
198 // Calls CancelAuth from the request_loop.
199 void CancelAuthDeferred() {
Evan Martin 2009/06/19 21:55:48 It's a bit weird that all of these are inside the
200 DCHECK(MessageLoop::current() == request_loop_);
201
202 if (request_) {
203 request_->CancelAuth();
204 // Verify that CancelAuth does destroy the request via our delegate.
205 DCHECK(request_ != NULL);
206 ResetLoginHandlerForRequest(request_);
207 }
208 }
209
210 // Closes the view_contents from the UI loop.
211 void CloseContentsDeferred() {
212 DCHECK(MessageLoop::current() == ui_loop_);
213
214 // The hosting ConstrainedWindow may have been freed.
215 if (dialog_)
216 dialog_->CloseConstrainedWindow();
217 }
218
219 // Returns whether authentication had been handled (SetAuth or CancelAuth).
220 // If |set_handled| is true, it will mark authentication as handled.
221 bool WasAuthHandled(bool set_handled) {
222 AutoLock lock(handled_auth_lock_);
223 bool was_handled = handled_auth_;
224 if (set_handled)
225 handled_auth_ = true;
226 return was_handled;
227 }
228
229 // Notify observers that authentication is needed or received. The automation
230 // proxy uses this for testing.
231 void SendNotifications() {
232 DCHECK(MessageLoop::current() == ui_loop_);
233
234 NotificationService* service = NotificationService::current();
235 TabContents* requesting_contents = GetTabContentsForLogin();
236 if (!requesting_contents)
237 return;
238
239 NavigationController* controller = &requesting_contents->controller();
240
241 if (!WasAuthHandled(false)) {
242 LoginNotificationDetails details(this);
243 service->Notify(NotificationType::AUTH_NEEDED,
244 Source<NavigationController>(controller),
245 Details<LoginNotificationDetails>(&details));
246 } else {
247 service->Notify(NotificationType::AUTH_SUPPLIED,
248 Source<NavigationController>(controller),
249 NotificationService::NoDetails());
250 }
251 }
252
253 static void OnOKClicked(GtkButton *button, LoginHandlerGtk* handler) {
254 DCHECK(MessageLoop::current() == handler->ui_loop_);
255
256 handler->SetAuth(
257 UTF8ToWide(gtk_entry_get_text(GTK_ENTRY(handler->username_entry_))),
258 UTF8ToWide(gtk_entry_get_text(GTK_ENTRY(handler->password_entry_))));
259 }
260
261 static void OnCancelClicked(GtkButton *button, LoginHandlerGtk* handler) {
262 DCHECK(MessageLoop::current() == handler->ui_loop_);
263 handler->CancelAuth();
264 }
265
266 // True if we've handled auth (SetAuth or CancelAuth has been called).
267 bool handled_auth_;
268 Lock handled_auth_lock_;
269
270 // The ConstrainedWindow that is hosting our LoginView.
271 // This should only be accessed on the ui_loop_.
272 ConstrainedWindow* dialog_;
273
274 // The MessageLoop of the thread that the ChromeViewContents lives in.
275 MessageLoop* ui_loop_;
276
277 // The request that wants login data.
278 // This should only be accessed on the request_loop_.
279 URLRequest* request_;
280
281 // The MessageLoop of the thread that the URLRequest lives in.
282 MessageLoop* request_loop_;
283
284 // The PasswordForm sent to the PasswordManager. This is so we can refer to it
285 // when later notifying the password manager if the credentials were accepted
286 // or rejected.
287 // This should only be accessed on the ui_loop_.
288 PasswordForm password_form_;
289
290 // Points to the password manager owned by the TabContents requesting auth.
291 // Can be null if the TabContents is not a TabContents.
292 // This should only be accessed on the ui_loop_.
293 PasswordManager* password_manager_;
294
295 // Cached from the URLRequest, in case it goes NULL on us.
296 int render_process_host_id_;
297 int tab_contents_id_;
298
299 // The GtkWidgets that form our visual hierarchy:
300 // The root container we pass to our parent.
301 OwnedWidgetGtk root_;
302
303 // GtkEntry widgets that the user types into.
304 GtkWidget* username_entry_;
305 GtkWidget* password_entry_;
306
307 // The action buttons on the bottom.
308 GtkWidget* ok_;
309 GtkWidget* cancel_;
Evan Martin 2009/06/19 21:55:48 These don't need to be members, it seems -- they'r
310
311 DISALLOW_COPY_AND_ASSIGN(LoginHandlerGtk);
312 };
313
314 // static
315 LoginHandler* LoginHandler::Create(URLRequest* request, MessageLoop* ui_loop) {
316 return new LoginHandlerGtk(request, ui_loop);
317 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698