1// Copyright (c) 2009 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#include "chrome/browser/ui/login/login_prompt.h"
6#import "chrome/browser/ui/login/login_prompt_mac.h"
7
8#include "base/mac/mac_util.h"
9#include "base/string_util.h"
10#include "base/sys_string_conversions.h"
11#include "base/utf_string_conversions.h"
12#include "chrome/browser/password_manager/password_manager.h"
13#include "chrome/browser/tab_contents/tab_util.h"
14#include "chrome/browser/ui/cocoa/constrained_window_mac.h"
15#include "chrome/browser/ui/login/login_model.h"
16#include "content/browser/browser_thread.h"
17#include "content/browser/renderer_host/resource_dispatcher_host.h"
18#include "content/browser/tab_contents/navigation_controller.h"
19#include "content/browser/tab_contents/tab_contents.h"
20#include "grit/generated_resources.h"
21#include "net/url_request/url_request.h"
22#include "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h"
23#include "ui/base/l10n/l10n_util.h"
24
25using webkit_glue::PasswordForm;
26
27// ----------------------------------------------------------------------------
28// LoginHandlerMac
29
30// This class simply forwards the authentication from the LoginView (on
31// the UI thread) to the net::URLRequest (on the I/O thread).
32// This class uses ref counting to ensure that it lives until all InvokeLaters
33// have been called.
34class LoginHandlerMac : public LoginHandler,
35                        public ConstrainedWindowMacDelegateCustomSheet {
36 public:
37  LoginHandlerMac(net::AuthChallengeInfo* auth_info, net::URLRequest* request)
38      : LoginHandler(auth_info, request),
39        sheet_controller_(nil) {
40  }
41
42  virtual ~LoginHandlerMac() {
43  }
44
45  // LoginModelObserver implementation.
46  virtual void OnAutofillDataAvailable(const std::wstring& username,
47                                       const std::wstring& password) {
48    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
49
50    [sheet_controller_ autofillLogin:base::SysWideToNSString(username)
51                            password:base::SysWideToNSString(password)];
52  }
53
54  // LoginHandler:
55  virtual void BuildViewForPasswordManager(PasswordManager* manager,
56                                           const string16& explanation) {
57    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
58
59    // Load nib here instead of in constructor.
60    sheet_controller_ = [[[LoginHandlerSheet alloc]
61        initWithLoginHandler:this] autorelease];
62    init([sheet_controller_ window], sheet_controller_,
63          @selector(sheetDidEnd:returnCode:contextInfo:));
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    SetDialog(GetTabContentsForLogin()->CreateConstrainedDialog(this));
75
76    NotifyAuthNeeded();
77  }
78
79  // Overridden from ConstrainedWindowMacDelegate:
80  virtual void DeleteDelegate() {
81    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
82
83    // The constrained window is going to delete itself; clear our pointer.
84    SetDialog(NULL);
85    SetModel(NULL);
86
87    // Close sheet if it's still open, as required by
88    // ConstrainedWindowMacDelegate.
89    if (is_sheet_open())
90      [NSApp endSheet:sheet()];
91
92    ReleaseSoon();
93  }
94
95  void OnLoginPressed(const string16& username,
96                      const string16& password) {
97    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
98
99    SetAuth(username, password);
100  }
101
102  void OnCancelPressed() {
103    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
104
105    CancelAuth();
106  }
107
108 private:
109  friend class LoginPrompt;
110
111  // The Cocoa controller of the GUI.
112  LoginHandlerSheet* sheet_controller_;
113
114  DISALLOW_COPY_AND_ASSIGN(LoginHandlerMac);
115};
116
117// static
118LoginHandler* LoginHandler::Create(net::AuthChallengeInfo* auth_info,
119                                   net::URLRequest* request) {
120  return new LoginHandlerMac(auth_info, request);
121}
122
123// ----------------------------------------------------------------------------
124// LoginHandlerSheet
125
126@implementation LoginHandlerSheet
127
128- (id)initWithLoginHandler:(LoginHandlerMac*)handler {
129  NSString* nibPath =
130      [base::mac::MainAppBundle() pathForResource:@"HttpAuthLoginSheet"
131                                          ofType:@"nib"];
132  if ((self = [super initWithWindowNibPath:nibPath
133                                     owner:self])) {
134    handler_ = handler;
135  }
136  return self;
137}
138
139- (void)dealloc {
140  // The buttons could be in a modal loop, so disconnect them so they cannot
141  // call back to us after we're dead.
142  [loginButton_ setTarget:nil];
143  [cancelButton_ setTarget:nil];
144  [super dealloc];
145}
146
147- (IBAction)loginPressed:(id)sender {
148  using base::SysNSStringToWide;
149  [NSApp endSheet:[self window]];
150  handler_->OnLoginPressed(
151      base::SysNSStringToUTF16([nameField_ stringValue]),
152      base::SysNSStringToUTF16([passwordField_ stringValue]));
153}
154
155- (IBAction)cancelPressed:(id)sender {
156  [NSApp endSheet:[self window]];
157  handler_->OnCancelPressed();
158}
159
160- (void)sheetDidEnd:(NSWindow*)sheet
161         returnCode:(int)returnCode
162        contextInfo:(void *)contextInfo {
163  [sheet orderOut:self];
164  // Also called when user navigates to another page while the sheet is open.
165}
166
167- (void)autofillLogin:(NSString*)login password:(NSString*)password {
168  if ([[nameField_ stringValue] length] == 0) {
169    [nameField_ setStringValue:login];
170    [passwordField_ setStringValue:password];
171    [nameField_ selectText:self];
172  }
173}
174
175- (void)setExplanation:(NSString*)explanation {
176  // Put in the text.
177  [explanationField_ setStringValue:explanation];
178
179  // Resize the TextField.
180  CGFloat explanationShift =
181      [GTMUILocalizerAndLayoutTweaker
182       sizeToFitFixedWidthTextField:explanationField_];
183
184  // Resize the window (no shifting needed due to window layout).
185  NSSize windowDelta = NSMakeSize(0, explanationShift);
186  [GTMUILocalizerAndLayoutTweaker
187      resizeWindowWithoutAutoResizingSubViews:[self window]
188                                        delta:windowDelta];
189}
190
191@end
192