Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 } | |
| OLD | NEW |