| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/login_prompt.h" | 5 #include "chrome/browser/login_prompt.h" |
| 6 | 6 |
| 7 #include "app/l10n_util.h" | 7 #include "app/l10n_util.h" |
| 8 #include "base/message_loop.h" | 8 #include "chrome/browser/chrome_thread.h" |
| 9 #include "chrome/browser/password_manager/password_manager.h" | 9 #include "chrome/browser/password_manager/password_manager.h" |
| 10 #include "chrome/browser/renderer_host/resource_dispatcher_host.h" | 10 #include "chrome/browser/renderer_host/resource_dispatcher_host.h" |
| 11 #include "chrome/browser/tab_contents/navigation_controller.h" | 11 #include "chrome/browser/tab_contents/navigation_controller.h" |
| 12 #include "chrome/browser/tab_contents/tab_contents.h" | 12 #include "chrome/browser/tab_contents/tab_contents.h" |
| 13 #include "chrome/browser/tab_contents/tab_util.h" | 13 #include "chrome/browser/tab_contents/tab_util.h" |
| 14 #include "chrome/browser/views/login_view.h" | 14 #include "chrome/browser/views/login_view.h" |
| 15 #include "chrome/common/notification_service.h" | 15 #include "chrome/common/notification_service.h" |
| 16 #include "grit/generated_resources.h" | 16 #include "grit/generated_resources.h" |
| 17 #include "net/url_request/url_request.h" | 17 #include "net/url_request/url_request.h" |
| 18 #include "views/window/dialog_delegate.h" | 18 #include "views/window/dialog_delegate.h" |
| 19 | 19 |
| 20 using webkit_glue::PasswordForm; | 20 using webkit_glue::PasswordForm; |
| 21 | 21 |
| 22 // ---------------------------------------------------------------------------- | 22 // ---------------------------------------------------------------------------- |
| 23 // LoginHandlerWin | 23 // LoginHandlerWin |
| 24 | 24 |
| 25 // This class simply forwards the authentication from the LoginView (on | 25 // This class simply forwards the authentication from the LoginView (on |
| 26 // the UI thread) to the URLRequest (on the I/O thread). | 26 // the UI thread) to the URLRequest (on the I/O thread). |
| 27 // This class uses ref counting to ensure that it lives until all InvokeLaters | 27 // This class uses ref counting to ensure that it lives until all InvokeLaters |
| 28 // have been called. | 28 // have been called. |
| 29 class LoginHandlerWin : public LoginHandler, | 29 class LoginHandlerWin : public LoginHandler, |
| 30 public base::RefCountedThreadSafe<LoginHandlerWin>, | 30 public base::RefCountedThreadSafe<LoginHandlerWin>, |
| 31 public views::DialogDelegate { | 31 public views::DialogDelegate { |
| 32 public: | 32 public: |
| 33 LoginHandlerWin(URLRequest* request, MessageLoop* ui_loop) | 33 LoginHandlerWin(URLRequest* request) |
| 34 : dialog_(NULL), | 34 : dialog_(NULL), |
| 35 handled_auth_(false), | 35 handled_auth_(false), |
| 36 request_(request), | 36 request_(request), |
| 37 request_loop_(MessageLoop::current()), | |
| 38 ui_loop_(ui_loop), | |
| 39 password_manager_(NULL) { | 37 password_manager_(NULL) { |
| 40 DCHECK(request_) << "LoginHandler constructed with NULL request"; | 38 DCHECK(request_) << "LoginHandler constructed with NULL request"; |
| 41 | 39 |
| 42 AddRef(); // matched by ReleaseLater. | 40 AddRef(); // matched by ReleaseLater. |
| 43 if (!ResourceDispatcherHost::RenderViewForRequest(request_, | 41 if (!ResourceDispatcherHost::RenderViewForRequest(request_, |
| 44 &render_process_host_id_, | 42 &render_process_host_id_, |
| 45 &tab_contents_id_)) { | 43 &tab_contents_id_)) { |
| 46 NOTREACHED(); | 44 NOTREACHED(); |
| 47 } | 45 } |
| 48 } | 46 } |
| 49 | 47 |
| 50 ~LoginHandlerWin() { | 48 ~LoginHandlerWin() { |
| 51 } | 49 } |
| 52 | 50 |
| 53 void set_login_view(LoginView* login_view) { | 51 void set_login_view(LoginView* login_view) { |
| 54 login_view_ = login_view; | 52 login_view_ = login_view; |
| 55 } | 53 } |
| 56 | 54 |
| 57 // views::DialogDelegate methods: | 55 // views::DialogDelegate methods: |
| 58 virtual std::wstring GetDialogButtonLabel( | 56 virtual std::wstring GetDialogButtonLabel( |
| 59 MessageBoxFlags::DialogButton button) const { | 57 MessageBoxFlags::DialogButton button) const { |
| 60 if (button == MessageBoxFlags::DIALOGBUTTON_OK) | 58 if (button == MessageBoxFlags::DIALOGBUTTON_OK) |
| 61 return l10n_util::GetString(IDS_LOGIN_DIALOG_OK_BUTTON_LABEL); | 59 return l10n_util::GetString(IDS_LOGIN_DIALOG_OK_BUTTON_LABEL); |
| 62 return DialogDelegate::GetDialogButtonLabel(button); | 60 return DialogDelegate::GetDialogButtonLabel(button); |
| 63 } | 61 } |
| 64 virtual std::wstring GetWindowTitle() const { | 62 virtual std::wstring GetWindowTitle() const { |
| 65 return l10n_util::GetString(IDS_LOGIN_DIALOG_TITLE); | 63 return l10n_util::GetString(IDS_LOGIN_DIALOG_TITLE); |
| 66 } | 64 } |
| 67 virtual void WindowClosing() { | 65 virtual void WindowClosing() { |
| 68 DCHECK(MessageLoop::current() == ui_loop_); | 66 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| 69 | 67 |
| 70 // Reference is no longer valid. | 68 // Reference is no longer valid. |
| 71 dialog_ = NULL; | 69 dialog_ = NULL; |
| 72 | 70 |
| 73 if (!WasAuthHandled(true)) { | 71 if (!WasAuthHandled(true)) { |
| 74 request_loop_->PostTask(FROM_HERE, NewRunnableMethod( | 72 ChromeThread::PostTask( |
| 75 this, &LoginHandlerWin::CancelAuthDeferred)); | 73 ChromeThread::IO, FROM_HERE, |
| 74 NewRunnableMethod(this, &LoginHandlerWin::CancelAuthDeferred)); |
| 76 SendNotifications(); | 75 SendNotifications(); |
| 77 } | 76 } |
| 78 } | 77 } |
| 79 virtual void DeleteDelegate() { | 78 virtual void DeleteDelegate() { |
| 80 // Delete this object once all InvokeLaters have been called. | 79 // Delete this object once all InvokeLaters have been called. |
| 81 request_loop_->ReleaseSoon(FROM_HERE, this); | 80 ChromeThread::ReleaseSoon(ChromeThread::IO, FROM_HERE, this); |
| 82 } | 81 } |
| 83 virtual bool Cancel() { | 82 virtual bool Cancel() { |
| 84 DCHECK(MessageLoop::current() == ui_loop_); | 83 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| 85 DCHECK(dialog_) << "LoginHandler invoked without being attached"; | 84 DCHECK(dialog_) << "LoginHandler invoked without being attached"; |
| 86 CancelAuth(); | 85 CancelAuth(); |
| 87 return true; | 86 return true; |
| 88 } | 87 } |
| 89 virtual bool Accept() { | 88 virtual bool Accept() { |
| 90 DCHECK(MessageLoop::current() == ui_loop_); | 89 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| 91 DCHECK(dialog_) << "LoginHandler invoked without being attached"; | 90 DCHECK(dialog_) << "LoginHandler invoked without being attached"; |
| 92 SetAuth(login_view_->GetUsername(), login_view_->GetPassword()); | 91 SetAuth(login_view_->GetUsername(), login_view_->GetPassword()); |
| 93 return true; | 92 return true; |
| 94 } | 93 } |
| 95 virtual views::View* GetContentsView() { | 94 virtual views::View* GetContentsView() { |
| 96 return login_view_; | 95 return login_view_; |
| 97 } | 96 } |
| 98 | 97 |
| 99 // LoginHandler: | 98 // LoginHandler: |
| 100 | 99 |
| 101 virtual void BuildViewForPasswordManager(PasswordManager* manager, | 100 virtual void BuildViewForPasswordManager(PasswordManager* manager, |
| 102 std::wstring explanation) { | 101 std::wstring explanation) { |
| 103 DCHECK(MessageLoop::current() == ui_loop_); | 102 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| 104 | 103 |
| 105 LoginView* view = new LoginView(explanation); | 104 LoginView* view = new LoginView(explanation); |
| 106 | 105 |
| 107 // Set the model for the login view. The model (password manager) is owned | 106 // Set the model for the login view. The model (password manager) is owned |
| 108 // by the view's parent TabContents, so natural destruction order means we | 107 // by the view's parent TabContents, so natural destruction order means we |
| 109 // don't have to worry about calling SetModel(NULL), because the view will | 108 // don't have to worry about calling SetModel(NULL), because the view will |
| 110 // be deleted before the password manager. | 109 // be deleted before the password manager. |
| 111 view->SetModel(manager); | 110 view->SetModel(manager); |
| 112 | 111 |
| 113 set_login_view(view); | 112 set_login_view(view); |
| 114 | 113 |
| 115 // Scary thread safety note: This can potentially be called *after* SetAuth | 114 // 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 | 115 // 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 | 116 // 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 | 117 // 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). | 118 // to happen after this is called (since this was InvokeLater'd first). |
| 120 dialog_ = GetTabContentsForLogin()->CreateConstrainedDialog(this); | 119 dialog_ = GetTabContentsForLogin()->CreateConstrainedDialog(this); |
| 121 SendNotifications(); | 120 SendNotifications(); |
| 122 } | 121 } |
| 123 | 122 |
| 124 virtual void SetPasswordForm(const webkit_glue::PasswordForm& form) { | 123 virtual void SetPasswordForm(const webkit_glue::PasswordForm& form) { |
| 125 password_form_ = form; | 124 password_form_ = form; |
| 126 } | 125 } |
| 127 | 126 |
| 128 virtual void SetPasswordManager(PasswordManager* password_manager) { | 127 virtual void SetPasswordManager(PasswordManager* password_manager) { |
| 129 password_manager_ = password_manager; | 128 password_manager_ = password_manager; |
| 130 } | 129 } |
| 131 | 130 |
| 132 virtual TabContents* GetTabContentsForLogin() { | 131 virtual TabContents* GetTabContentsForLogin() { |
| 133 DCHECK(MessageLoop::current() == ui_loop_); | 132 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| 134 | 133 |
| 135 return tab_util::GetTabContentsByID(render_process_host_id_, | 134 return tab_util::GetTabContentsByID(render_process_host_id_, |
| 136 tab_contents_id_); | 135 tab_contents_id_); |
| 137 } | 136 } |
| 138 | 137 |
| 139 virtual void SetAuth(const std::wstring& username, | 138 virtual void SetAuth(const std::wstring& username, |
| 140 const std::wstring& password) { | 139 const std::wstring& password) { |
| 141 if (WasAuthHandled(true)) | 140 if (WasAuthHandled(true)) |
| 142 return; | 141 return; |
| 143 | 142 |
| 144 // Tell the password manager the credentials were submitted / accepted. | 143 // Tell the password manager the credentials were submitted / accepted. |
| 145 if (password_manager_) { | 144 if (password_manager_) { |
| 146 password_form_.username_value = username; | 145 password_form_.username_value = username; |
| 147 password_form_.password_value = password; | 146 password_form_.password_value = password; |
| 148 password_manager_->ProvisionallySavePassword(password_form_); | 147 password_manager_->ProvisionallySavePassword(password_form_); |
| 149 } | 148 } |
| 150 | 149 |
| 151 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod( | 150 ChromeThread::PostTask( |
| 152 this, &LoginHandlerWin::CloseContentsDeferred)); | 151 ChromeThread::UI, FROM_HERE, |
| 153 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod( | 152 NewRunnableMethod(this, &LoginHandlerWin::CloseContentsDeferred)); |
| 154 this, &LoginHandlerWin::SendNotifications)); | 153 ChromeThread::PostTask( |
| 155 request_loop_->PostTask(FROM_HERE, NewRunnableMethod( | 154 ChromeThread::UI, FROM_HERE, |
| 156 this, &LoginHandlerWin::SetAuthDeferred, username, password)); | 155 NewRunnableMethod(this, &LoginHandlerWin::SendNotifications)); |
| 156 ChromeThread::PostTask( |
| 157 ChromeThread::IO, FROM_HERE, |
| 158 NewRunnableMethod( |
| 159 this, &LoginHandlerWin::SetAuthDeferred, username, password)); |
| 157 } | 160 } |
| 158 | 161 |
| 159 virtual void CancelAuth() { | 162 virtual void CancelAuth() { |
| 160 if (WasAuthHandled(true)) | 163 if (WasAuthHandled(true)) |
| 161 return; | 164 return; |
| 162 | 165 |
| 163 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod( | 166 ChromeThread::PostTask( |
| 164 this, &LoginHandlerWin::CloseContentsDeferred)); | 167 ChromeThread::UI, FROM_HERE, |
| 165 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod( | 168 NewRunnableMethod(this, &LoginHandlerWin::CloseContentsDeferred)); |
| 166 this, &LoginHandlerWin::SendNotifications)); | 169 ChromeThread::PostTask( |
| 167 request_loop_->PostTask(FROM_HERE, NewRunnableMethod( | 170 ChromeThread::UI, FROM_HERE, |
| 168 this, &LoginHandlerWin::CancelAuthDeferred)); | 171 NewRunnableMethod(this, &LoginHandlerWin::SendNotifications)); |
| 172 ChromeThread::PostTask( |
| 173 ChromeThread::IO, FROM_HERE, |
| 174 NewRunnableMethod(this, &LoginHandlerWin::CancelAuthDeferred)); |
| 169 } | 175 } |
| 170 | 176 |
| 171 virtual void OnRequestCancelled() { | 177 virtual void OnRequestCancelled() { |
| 172 DCHECK(MessageLoop::current() == request_loop_) << | 178 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)) << |
| 173 "Why is OnRequestCancelled called from the UI thread?"; | 179 "Why is OnRequestCancelled called from the UI thread?"; |
| 174 | 180 |
| 175 // Reference is no longer valid. | 181 // Reference is no longer valid. |
| 176 request_ = NULL; | 182 request_ = NULL; |
| 177 | 183 |
| 178 // Give up on auth if the request was cancelled. | 184 // Give up on auth if the request was cancelled. |
| 179 CancelAuth(); | 185 CancelAuth(); |
| 180 } | 186 } |
| 181 | 187 |
| 182 private: | 188 private: |
| 183 friend class LoginPrompt; | 189 friend class LoginPrompt; |
| 184 | 190 |
| 185 // Calls SetAuth from the request_loop. | 191 // Calls SetAuth from the IO loop. |
| 186 void SetAuthDeferred(const std::wstring& username, | 192 void SetAuthDeferred(const std::wstring& username, |
| 187 const std::wstring& password) { | 193 const std::wstring& password) { |
| 188 DCHECK(MessageLoop::current() == request_loop_); | 194 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); |
| 189 | 195 |
| 190 if (request_) { | 196 if (request_) { |
| 191 request_->SetAuth(username, password); | 197 request_->SetAuth(username, password); |
| 192 ResetLoginHandlerForRequest(request_); | 198 ResetLoginHandlerForRequest(request_); |
| 193 } | 199 } |
| 194 } | 200 } |
| 195 | 201 |
| 196 // Calls CancelAuth from the request_loop. | 202 // Calls CancelAuth from the IO loop. |
| 197 void CancelAuthDeferred() { | 203 void CancelAuthDeferred() { |
| 198 DCHECK(MessageLoop::current() == request_loop_); | 204 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); |
| 199 | 205 |
| 200 if (request_) { | 206 if (request_) { |
| 201 request_->CancelAuth(); | 207 request_->CancelAuth(); |
| 202 // Verify that CancelAuth does destroy the request via our delegate. | 208 // Verify that CancelAuth does destroy the request via our delegate. |
| 203 DCHECK(request_ != NULL); | 209 DCHECK(request_ != NULL); |
| 204 ResetLoginHandlerForRequest(request_); | 210 ResetLoginHandlerForRequest(request_); |
| 205 } | 211 } |
| 206 } | 212 } |
| 207 | 213 |
| 208 // Closes the view_contents from the UI loop. | 214 // Closes the view_contents from the UI loop. |
| 209 void CloseContentsDeferred() { | 215 void CloseContentsDeferred() { |
| 210 DCHECK(MessageLoop::current() == ui_loop_); | 216 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| 211 | 217 |
| 212 // The hosting ConstrainedWindow may have been freed. | 218 // The hosting ConstrainedWindow may have been freed. |
| 213 if (dialog_) | 219 if (dialog_) |
| 214 dialog_->CloseConstrainedWindow(); | 220 dialog_->CloseConstrainedWindow(); |
| 215 } | 221 } |
| 216 | 222 |
| 217 // Returns whether authentication had been handled (SetAuth or CancelAuth). | 223 // Returns whether authentication had been handled (SetAuth or CancelAuth). |
| 218 // If |set_handled| is true, it will mark authentication as handled. | 224 // If |set_handled| is true, it will mark authentication as handled. |
| 219 bool WasAuthHandled(bool set_handled) { | 225 bool WasAuthHandled(bool set_handled) { |
| 220 AutoLock lock(handled_auth_lock_); | 226 AutoLock lock(handled_auth_lock_); |
| 221 bool was_handled = handled_auth_; | 227 bool was_handled = handled_auth_; |
| 222 if (set_handled) | 228 if (set_handled) |
| 223 handled_auth_ = true; | 229 handled_auth_ = true; |
| 224 return was_handled; | 230 return was_handled; |
| 225 } | 231 } |
| 226 | 232 |
| 227 // Notify observers that authentication is needed or received. The automation | 233 // Notify observers that authentication is needed or received. The automation |
| 228 // proxy uses this for testing. | 234 // proxy uses this for testing. |
| 229 void SendNotifications() { | 235 void SendNotifications() { |
| 230 DCHECK(MessageLoop::current() == ui_loop_); | 236 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| 231 | 237 |
| 232 NotificationService* service = NotificationService::current(); | 238 NotificationService* service = NotificationService::current(); |
| 233 TabContents* requesting_contents = GetTabContentsForLogin(); | 239 TabContents* requesting_contents = GetTabContentsForLogin(); |
| 234 if (!requesting_contents) | 240 if (!requesting_contents) |
| 235 return; | 241 return; |
| 236 | 242 |
| 237 NavigationController* controller = &requesting_contents->controller(); | 243 NavigationController* controller = &requesting_contents->controller(); |
| 238 | 244 |
| 239 if (!WasAuthHandled(false)) { | 245 if (!WasAuthHandled(false)) { |
| 240 LoginNotificationDetails details(this); | 246 LoginNotificationDetails details(this); |
| 241 service->Notify(NotificationType::AUTH_NEEDED, | 247 service->Notify(NotificationType::AUTH_NEEDED, |
| 242 Source<NavigationController>(controller), | 248 Source<NavigationController>(controller), |
| 243 Details<LoginNotificationDetails>(&details)); | 249 Details<LoginNotificationDetails>(&details)); |
| 244 } else { | 250 } else { |
| 245 service->Notify(NotificationType::AUTH_SUPPLIED, | 251 service->Notify(NotificationType::AUTH_SUPPLIED, |
| 246 Source<NavigationController>(controller), | 252 Source<NavigationController>(controller), |
| 247 NotificationService::NoDetails()); | 253 NotificationService::NoDetails()); |
| 248 } | 254 } |
| 249 } | 255 } |
| 250 | 256 |
| 251 // True if we've handled auth (SetAuth or CancelAuth has been called). | 257 // True if we've handled auth (SetAuth or CancelAuth has been called). |
| 252 bool handled_auth_; | 258 bool handled_auth_; |
| 253 Lock handled_auth_lock_; | 259 Lock handled_auth_lock_; |
| 254 | 260 |
| 255 // The ConstrainedWindow that is hosting our LoginView. | 261 // The ConstrainedWindow that is hosting our LoginView. |
| 256 // This should only be accessed on the ui_loop_. | 262 // This should only be accessed on the UI loop. |
| 257 ConstrainedWindow* dialog_; | 263 ConstrainedWindow* dialog_; |
| 258 | 264 |
| 259 // The MessageLoop of the thread that the ChromeViewContents lives in. | |
| 260 MessageLoop* ui_loop_; | |
| 261 | |
| 262 // The request that wants login data. | 265 // The request that wants login data. |
| 263 // This should only be accessed on the request_loop_. | 266 // This should only be accessed on the IO loop. |
| 264 URLRequest* request_; | 267 URLRequest* request_; |
| 265 | 268 |
| 266 // The MessageLoop of the thread that the URLRequest lives in. | |
| 267 MessageLoop* request_loop_; | |
| 268 | |
| 269 // The LoginView that contains the user's login information | 269 // The LoginView that contains the user's login information |
| 270 LoginView* login_view_; | 270 LoginView* login_view_; |
| 271 | 271 |
| 272 // The PasswordForm sent to the PasswordManager. This is so we can refer to it | 272 // The PasswordForm sent to the PasswordManager. This is so we can refer to it |
| 273 // when later notifying the password manager if the credentials were accepted | 273 // when later notifying the password manager if the credentials were accepted |
| 274 // or rejected. | 274 // or rejected. |
| 275 // This should only be accessed on the ui_loop_. | 275 // This should only be accessed on the UI loop. |
| 276 PasswordForm password_form_; | 276 PasswordForm password_form_; |
| 277 | 277 |
| 278 // Points to the password manager owned by the TabContents requesting auth. | 278 // Points to the password manager owned by the TabContents requesting auth. |
| 279 // Can be null if the TabContents is not a TabContents. | 279 // Can be null if the TabContents is not a TabContents. |
| 280 // This should only be accessed on the ui_loop_. | 280 // This should only be accessed on the UI loop. |
| 281 PasswordManager* password_manager_; | 281 PasswordManager* password_manager_; |
| 282 | 282 |
| 283 // Cached from the URLRequest, in case it goes NULL on us. | 283 // Cached from the URLRequest, in case it goes NULL on us. |
| 284 int render_process_host_id_; | 284 int render_process_host_id_; |
| 285 int tab_contents_id_; | 285 int tab_contents_id_; |
| 286 | 286 |
| 287 DISALLOW_COPY_AND_ASSIGN(LoginHandlerWin); | 287 DISALLOW_COPY_AND_ASSIGN(LoginHandlerWin); |
| 288 }; | 288 }; |
| 289 | 289 |
| 290 // static | 290 // static |
| 291 LoginHandler* LoginHandler::Create(URLRequest* request, MessageLoop* ui_loop) { | 291 LoginHandler* LoginHandler::Create(URLRequest* request) { |
| 292 return new LoginHandlerWin(request, ui_loop); | 292 return new LoginHandlerWin(request); |
| 293 } | 293 } |
| OLD | NEW |