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