blob: dc3b6784f6a0da86e34f2c6c0bb608853955d292 [file] [log] [blame]
// Copyright (c) 2012 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.
#import "chrome/browser/ui/cocoa/login_prompt_cocoa.h"
#include "base/mac/bundle_locations.h"
#include "base/mac/mac_util.h"
#include "base/mac/scoped_nsobject.h"
#include "base/strings/string16.h"
#include "base/strings/string_util.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/password_manager/password_manager.h"
#include "chrome/browser/tab_contents/tab_util.h"
#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.h"
#include "chrome/browser/ui/cocoa/constrained_window/constrained_window_mac.h"
#include "chrome/browser/ui/login/login_model.h"
#include "chrome/browser/ui/login/login_prompt.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_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 content::BrowserThread;
using content::PasswordForm;
using content::WebContents;
// ----------------------------------------------------------------------------
// 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 ConstrainedWindowMacDelegate {
public:
LoginHandlerMac(net::AuthChallengeInfo* auth_info, net::URLRequest* request)
: LoginHandler(auth_info, request) {
}
// LoginModelObserver implementation.
virtual void OnAutofillDataAvailable(const string16& username,
const string16& password) OVERRIDE {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[sheet_controller_ autofillLogin:base::SysUTF16ToNSString(username)
password:base::SysUTF16ToNSString(password)];
}
virtual void OnLoginModelDestroying() OVERRIDE {}
// LoginHandler:
virtual void BuildViewForPasswordManager(
PasswordManager* manager,
const string16& explanation) OVERRIDE {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
sheet_controller_.reset(
[[LoginHandlerSheet alloc] initWithLoginHandler:this]);
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).
WebContents* requesting_contents = GetWebContentsForLogin();
DCHECK(requesting_contents);
base::scoped_nsobject<CustomConstrainedWindowSheet> sheet(
[[CustomConstrainedWindowSheet alloc]
initWithCustomWindow:[sheet_controller_ window]]);
constrained_window_.reset(new ConstrainedWindowMac(
this, requesting_contents, sheet));
NotifyAuthNeeded();
}
virtual void CloseDialog() OVERRIDE {
// The hosting dialog may have been freed.
if (constrained_window_)
constrained_window_->CloseWebContentsModalDialog();
}
// Overridden from ConstrainedWindowMacDelegate:
virtual void OnConstrainedWindowClosed(
ConstrainedWindowMac* window) OVERRIDE {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
SetModel(NULL);
ReleaseSoon();
constrained_window_.reset();
sheet_controller_.reset();
}
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;
virtual ~LoginHandlerMac() {
// This class will be deleted on a non UI thread. Ensure that the UI members
// have already been deleted.
CHECK(!constrained_window_.get());
CHECK(!sheet_controller_.get());
}
// The Cocoa controller of the GUI.
base::scoped_nsobject<LoginHandlerSheet> sheet_controller_;
scoped_ptr<ConstrainedWindowMac> constrained_window_;
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::FrameworkBundle() pathForResource:@"HttpAuthLoginSheet"
ofType:@"nib"];
if ((self = [super initWithWindowNibPath:nibPath
owner:self])) {
handler_ = handler;
// Force the nib to load so that all outlets are initialized.
[self window];
}
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 {
handler_->OnLoginPressed(
base::SysNSStringToUTF16([nameField_ stringValue]),
base::SysNSStringToUTF16([passwordField_ stringValue]));
}
- (IBAction)cancelPressed:(id)sender {
handler_->OnCancelPressed();
}
- (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 text field.
CGFloat windowDelta = [GTMUILocalizerAndLayoutTweaker
sizeToFitFixedWidthTextField:explanationField_];
NSRect newFrame = [[self window] frame];
newFrame.size.height += windowDelta;
[[self window] setFrame:newFrame display:NO];
}
@end