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