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