// Copyright (c) 2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/ui/login/login_prompt.h" #import "chrome/browser/ui/login/login_prompt_mac.h" #include "base/mac/mac_util.h" #include "base/string_util.h" #include "base/sys_string_conversions.h" #include "base/utf_string_conversions.h" #include "chrome/browser/password_manager/password_manager.h" #include "chrome/browser/tab_contents/tab_util.h" #include "chrome/browser/ui/cocoa/constrained_window_mac.h" #include "chrome/browser/ui/login/login_model.h" #include "content/browser/browser_thread.h" #include "content/browser/renderer_host/resource_dispatcher_host.h" #include "content/browser/tab_contents/navigation_controller.h" #include "content/browser/tab_contents/tab_contents.h" #include "grit/generated_resources.h" #include "net/url_request/url_request.h" #include "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h" #include "ui/base/l10n/l10n_util.h" using webkit_glue::PasswordForm; // ---------------------------------------------------------------------------- // LoginHandlerMac // This class simply forwards the authentication from the LoginView (on // the UI thread) to the net::URLRequest (on the I/O thread). // This class uses ref counting to ensure that it lives until all InvokeLaters // have been called. class LoginHandlerMac : public LoginHandler, public ConstrainedWindowMacDelegateCustomSheet { public: LoginHandlerMac(net::AuthChallengeInfo* auth_info, net::URLRequest* request) : LoginHandler(auth_info, request), sheet_controller_(nil) { } virtual ~LoginHandlerMac() { } // LoginModelObserver implementation. virtual void OnAutofillDataAvailable(const std::wstring& username, const std::wstring& password) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); [sheet_controller_ autofillLogin:base::SysWideToNSString(username) password:base::SysWideToNSString(password)]; } // LoginHandler: virtual void BuildViewForPasswordManager(PasswordManager* manager, const string16& explanation) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); // Load nib here instead of in constructor. sheet_controller_ = [[[LoginHandlerSheet alloc] initWithLoginHandler:this] autorelease]; init([sheet_controller_ window], sheet_controller_, @selector(sheetDidEnd:returnCode:contextInfo:)); SetModel(manager); [sheet_controller_ setExplanation:base::SysUTF16ToNSString(explanation)]; // Scary thread safety note: This can potentially be called *after* SetAuth // or CancelAuth (say, if the request was cancelled before the UI thread got // control). However, that's OK since any UI interaction in those functions // will occur via an InvokeLater on the UI thread, which is guaranteed // to happen after this is called (since this was InvokeLater'd first). SetDialog(GetTabContentsForLogin()->CreateConstrainedDialog(this)); NotifyAuthNeeded(); } // Overridden from ConstrainedWindowMacDelegate: virtual void DeleteDelegate() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); // The constrained window is going to delete itself; clear our pointer. SetDialog(NULL); SetModel(NULL); // Close sheet if it's still open, as required by // ConstrainedWindowMacDelegate. if (is_sheet_open()) [NSApp endSheet:sheet()]; ReleaseSoon(); } void OnLoginPressed(const string16& username, const string16& password) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); SetAuth(username, password); } void OnCancelPressed() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); CancelAuth(); } private: friend class LoginPrompt; // The Cocoa controller of the GUI. LoginHandlerSheet* sheet_controller_; DISALLOW_COPY_AND_ASSIGN(LoginHandlerMac); }; // static LoginHandler* LoginHandler::Create(net::AuthChallengeInfo* auth_info, net::URLRequest* request) { return new LoginHandlerMac(auth_info, request); } // ---------------------------------------------------------------------------- // LoginHandlerSheet @implementation LoginHandlerSheet - (id)initWithLoginHandler:(LoginHandlerMac*)handler { NSString* nibPath = [base::mac::MainAppBundle() pathForResource:@"HttpAuthLoginSheet" ofType:@"nib"]; if ((self = [super initWithWindowNibPath:nibPath owner:self])) { handler_ = handler; } return self; } - (void)dealloc { // The buttons could be in a modal loop, so disconnect them so they cannot // call back to us after we're dead. [loginButton_ setTarget:nil]; [cancelButton_ setTarget:nil]; [super dealloc]; } - (IBAction)loginPressed:(id)sender { using base::SysNSStringToWide; [NSApp endSheet:[self window]]; handler_->OnLoginPressed( base::SysNSStringToUTF16([nameField_ stringValue]), base::SysNSStringToUTF16([passwordField_ stringValue])); } - (IBAction)cancelPressed:(id)sender { [NSApp endSheet:[self window]]; handler_->OnCancelPressed(); } - (void)sheetDidEnd:(NSWindow*)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo { [sheet orderOut:self]; // Also called when user navigates to another page while the sheet is open. } - (void)autofillLogin:(NSString*)login password:(NSString*)password { if ([[nameField_ stringValue] length] == 0) { [nameField_ setStringValue:login]; [passwordField_ setStringValue:password]; [nameField_ selectText:self]; } } - (void)setExplanation:(NSString*)explanation { // Put in the text. [explanationField_ setStringValue:explanation]; // Resize the TextField. CGFloat explanationShift = [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:explanationField_]; // Resize the window (no shifting needed due to window layout). NSSize windowDelta = NSMakeSize(0, explanationShift); [GTMUILocalizerAndLayoutTweaker resizeWindowWithoutAutoResizingSubViews:[self window] delta:windowDelta]; } @end