Home | History | Annotate | Download | only in cocoa
      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