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 #import "chrome/browser/ui/cocoa/login_prompt_cocoa.h" 6 7 #include "base/mac/bundle_locations.h" 8 #include "base/mac/mac_util.h" 9 #include "base/mac/scoped_nsobject.h" 10 #include "base/strings/string16.h" 11 #include "base/strings/string_util.h" 12 #include "base/strings/sys_string_conversions.h" 13 #include "base/strings/utf_string_conversions.h" 14 #include "chrome/browser/tab_contents/tab_util.h" 15 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.h" 16 #include "chrome/browser/ui/cocoa/constrained_window/constrained_window_mac.h" 17 #include "chrome/browser/ui/login/login_prompt.h" 18 #include "components/password_manager/core/browser/login_model.h" 19 #include "components/password_manager/core/browser/password_manager.h" 20 #include "content/public/browser/browser_thread.h" 21 #include "content/public/browser/web_contents.h" 22 #include "net/url_request/url_request.h" 23 #include "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h" 24 25 using autofill::PasswordForm; 26 using content::BrowserThread; 27 using content::WebContents; 28 29 // ---------------------------------------------------------------------------- 30 // LoginHandlerMac 31 32 // This class simply forwards the authentication from the LoginView (on 33 // the UI thread) to the net::URLRequest (on the I/O thread). 34 // This class uses ref counting to ensure that it lives until all InvokeLaters 35 // have been called. 36 class LoginHandlerMac : public LoginHandler, 37 public ConstrainedWindowMacDelegate { 38 public: 39 LoginHandlerMac(net::AuthChallengeInfo* auth_info, net::URLRequest* request) 40 : LoginHandler(auth_info, request) { 41 } 42 43 // LoginModelObserver implementation. 44 virtual void OnAutofillDataAvailable( 45 const base::string16& username, 46 const base::string16& password) OVERRIDE { 47 DCHECK_CURRENTLY_ON(BrowserThread::UI); 48 49 [sheet_controller_ autofillLogin:base::SysUTF16ToNSString(username) 50 password:base::SysUTF16ToNSString(password)]; 51 } 52 virtual void OnLoginModelDestroying() OVERRIDE {} 53 54 // LoginHandler: 55 virtual void BuildViewForPasswordManager( 56 password_manager::PasswordManager* manager, 57 const base::string16& explanation) OVERRIDE { 58 DCHECK_CURRENTLY_ON(BrowserThread::UI); 59 60 sheet_controller_.reset( 61 [[LoginHandlerSheet alloc] initWithLoginHandler:this]); 62 63 SetModel(manager); 64 65 [sheet_controller_ setExplanation:base::SysUTF16ToNSString(explanation)]; 66 67 // Scary thread safety note: This can potentially be called *after* SetAuth 68 // or CancelAuth (say, if the request was cancelled before the UI thread got 69 // control). However, that's OK since any UI interaction in those functions 70 // will occur via an InvokeLater on the UI thread, which is guaranteed 71 // to happen after this is called (since this was InvokeLater'd first). 72 WebContents* requesting_contents = GetWebContentsForLogin(); 73 DCHECK(requesting_contents); 74 75 base::scoped_nsobject<CustomConstrainedWindowSheet> sheet( 76 [[CustomConstrainedWindowSheet alloc] 77 initWithCustomWindow:[sheet_controller_ window]]); 78 constrained_window_.reset(new ConstrainedWindowMac( 79 this, requesting_contents, sheet)); 80 81 NotifyAuthNeeded(); 82 } 83 84 virtual void CloseDialog() OVERRIDE { 85 // The hosting dialog may have been freed. 86 if (constrained_window_) 87 constrained_window_->CloseWebContentsModalDialog(); 88 } 89 90 // Overridden from ConstrainedWindowMacDelegate: 91 virtual void OnConstrainedWindowClosed( 92 ConstrainedWindowMac* window) OVERRIDE { 93 DCHECK_CURRENTLY_ON(BrowserThread::UI); 94 SetModel(NULL); 95 ReleaseSoon(); 96 97 constrained_window_.reset(); 98 sheet_controller_.reset(); 99 } 100 101 void OnLoginPressed(const base::string16& username, 102 const base::string16& password) { 103 DCHECK_CURRENTLY_ON(BrowserThread::UI); 104 SetAuth(username, password); 105 } 106 107 void OnCancelPressed() { 108 DCHECK_CURRENTLY_ON(BrowserThread::UI); 109 CancelAuth(); 110 } 111 112 private: 113 friend class LoginPrompt; 114 115 virtual ~LoginHandlerMac() { 116 // This class will be deleted on a non UI thread. Ensure that the UI members 117 // have already been deleted. 118 CHECK(!constrained_window_.get()); 119 CHECK(!sheet_controller_.get()); 120 } 121 122 // The Cocoa controller of the GUI. 123 base::scoped_nsobject<LoginHandlerSheet> sheet_controller_; 124 125 scoped_ptr<ConstrainedWindowMac> constrained_window_; 126 127 DISALLOW_COPY_AND_ASSIGN(LoginHandlerMac); 128 }; 129 130 // static 131 LoginHandler* LoginHandler::Create(net::AuthChallengeInfo* auth_info, 132 net::URLRequest* request) { 133 return new LoginHandlerMac(auth_info, request); 134 } 135 136 // ---------------------------------------------------------------------------- 137 // LoginHandlerSheet 138 139 @implementation LoginHandlerSheet 140 141 - (id)initWithLoginHandler:(LoginHandlerMac*)handler { 142 NSString* nibPath = 143 [base::mac::FrameworkBundle() pathForResource:@"HttpAuthLoginSheet" 144 ofType:@"nib"]; 145 if ((self = [super initWithWindowNibPath:nibPath 146 owner:self])) { 147 handler_ = handler; 148 // Force the nib to load so that all outlets are initialized. 149 [self window]; 150 } 151 return self; 152 } 153 154 - (void)dealloc { 155 // The buttons could be in a modal loop, so disconnect them so they cannot 156 // call back to us after we're dead. 157 [loginButton_ setTarget:nil]; 158 [cancelButton_ setTarget:nil]; 159 [super dealloc]; 160 } 161 162 - (IBAction)loginPressed:(id)sender { 163 handler_->OnLoginPressed( 164 base::SysNSStringToUTF16([nameField_ stringValue]), 165 base::SysNSStringToUTF16([passwordField_ stringValue])); 166 } 167 168 - (IBAction)cancelPressed:(id)sender { 169 handler_->OnCancelPressed(); 170 } 171 172 - (void)autofillLogin:(NSString*)login password:(NSString*)password { 173 if ([[nameField_ stringValue] length] == 0) { 174 [nameField_ setStringValue:login]; 175 [passwordField_ setStringValue:password]; 176 [nameField_ selectText:self]; 177 } 178 } 179 180 - (void)setExplanation:(NSString*)explanation { 181 // Put in the text. 182 [explanationField_ setStringValue:explanation]; 183 184 // Resize the text field. 185 CGFloat windowDelta = [GTMUILocalizerAndLayoutTweaker 186 sizeToFitFixedWidthTextField:explanationField_]; 187 188 NSRect newFrame = [[self window] frame]; 189 newFrame.size.height += windowDelta; 190 [[self window] setFrame:newFrame display:NO]; 191 } 192 193 @end 194