Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / ssl_client_certificate_selector_cocoa.mm
blobc7ad387c0b82312886beeb1af53e222ad05465ea
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.
5 #import "chrome/browser/ui/cocoa/ssl_client_certificate_selector_cocoa.h"
7 #import <SecurityInterface/SFChooseIdentityPanel.h>
9 #include "base/logging.h"
10 #include "base/mac/foundation_util.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/ssl/ssl_client_auth_observer.h"
15 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_mac.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/web_contents.h"
18 #include "content/public/browser/web_contents_view.h"
19 #include "grit/generated_resources.h"
20 #include "net/cert/x509_certificate.h"
21 #include "net/cert/x509_util_mac.h"
22 #include "net/ssl/ssl_cert_request_info.h"
23 #include "ui/base/cocoa/window_size_constants.h"
24 #include "ui/base/l10n/l10n_util_mac.h"
26 using content::BrowserThread;
28 @interface SFChooseIdentityPanel (SystemPrivate)
29 // A system-private interface that dismisses a panel whose sheet was started by
30 // -beginSheetForWindow:modalDelegate:didEndSelector:contextInfo:identities:message:
31 // as though the user clicked the button identified by returnCode. Verified
32 // present in 10.5 through 10.8.
33 - (void)_dismissWithCode:(NSInteger)code;
34 @end
36 @interface SSLClientCertificateSelectorCocoa ()
37 - (void)onConstrainedWindowClosed;
38 @end
40 class SSLClientAuthObserverCocoaBridge : public SSLClientAuthObserver,
41                                          public ConstrainedWindowMacDelegate {
42  public:
43   SSLClientAuthObserverCocoaBridge(
44       const net::HttpNetworkSession* network_session,
45       net::SSLCertRequestInfo* cert_request_info,
46       const chrome::SelectCertificateCallback& callback,
47       SSLClientCertificateSelectorCocoa* controller)
48       : SSLClientAuthObserver(network_session, cert_request_info, callback),
49         controller_(controller) {
50   }
52   // SSLClientAuthObserver implementation:
53   virtual void OnCertSelectedByNotification() OVERRIDE {
54     [controller_ closeWebContentsModalDialog];
55   }
57   // ConstrainedWindowMacDelegate implementation:
58   virtual void OnConstrainedWindowClosed(
59       ConstrainedWindowMac* window) OVERRIDE {
60     // |onConstrainedWindowClosed| will delete the sheet which might be still
61     // in use higher up the call stack. Wait for the next cycle of the event
62     // loop to call this function.
63     [controller_ performSelector:@selector(onConstrainedWindowClosed)
64                       withObject:nil
65                       afterDelay:0];
66   }
68  private:
69   SSLClientCertificateSelectorCocoa* controller_;  // weak
72 namespace chrome {
74 void ShowSSLClientCertificateSelector(
75     content::WebContents* contents,
76     const net::HttpNetworkSession* network_session,
77     net::SSLCertRequestInfo* cert_request_info,
78     const SelectCertificateCallback& callback) {
79   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
80   // The dialog manages its own lifetime.
81   SSLClientCertificateSelectorCocoa* selector =
82       [[SSLClientCertificateSelectorCocoa alloc]
83           initWithNetworkSession:network_session
84                  certRequestInfo:cert_request_info
85                         callback:callback];
86   [selector displayForWebContents:contents];
89 }  // namespace chrome
91 @implementation SSLClientCertificateSelectorCocoa
93 - (id)initWithNetworkSession:(const net::HttpNetworkSession*)networkSession
94     certRequestInfo:(net::SSLCertRequestInfo*)certRequestInfo
95            callback:(const chrome::SelectCertificateCallback&)callback {
96   DCHECK(networkSession);
97   DCHECK(certRequestInfo);
98   if ((self = [super init])) {
99     observer_.reset(new SSLClientAuthObserverCocoaBridge(
100         networkSession, certRequestInfo, callback, self));
101   }
102   return self;
105 - (void)sheetDidEnd:(NSWindow*)parent
106          returnCode:(NSInteger)returnCode
107             context:(void*)context {
108   net::X509Certificate* cert = NULL;
109   if (returnCode == NSFileHandlingPanelOKButton) {
110     CFRange range = CFRangeMake(0, CFArrayGetCount(identities_));
111     CFIndex index =
112         CFArrayGetFirstIndexOfValue(identities_, range, [panel_ identity]);
113     if (index != -1)
114       cert = certificates_[index].get();
115     else
116       NOTREACHED();
117   }
119   // Finally, tell the backend which identity (or none) the user selected.
120   observer_->StopObserving();
121   observer_->CertificateSelected(cert);
123   if (!closePending_)
124     constrainedWindow_->CloseWebContentsModalDialog();
127 - (void)displayForWebContents:(content::WebContents*)webContents {
128   // Create an array of CFIdentityRefs for the certificates:
129   size_t numCerts = observer_->cert_request_info()->client_certs.size();
130   identities_.reset(CFArrayCreateMutable(
131       kCFAllocatorDefault, numCerts, &kCFTypeArrayCallBacks));
132   for (size_t i = 0; i < numCerts; ++i) {
133     SecCertificateRef cert =
134         observer_->cert_request_info()->client_certs[i]->os_cert_handle();
135     SecIdentityRef identity;
136     if (SecIdentityCreateWithCertificate(NULL, cert, &identity) == noErr) {
137       CFArrayAppendValue(identities_, identity);
138       CFRelease(identity);
139       certificates_.push_back(observer_->cert_request_info()->client_certs[i]);
140     }
141   }
143   // Get the message to display:
144   NSString* message = l10n_util::GetNSStringF(
145       IDS_CLIENT_CERT_DIALOG_TEXT,
146       base::ASCIIToUTF16(
147           observer_->cert_request_info()->host_and_port.ToString()));
149   // Create and set up a system choose-identity panel.
150   panel_.reset([[SFChooseIdentityPanel alloc] init]);
151   [panel_ setInformativeText:message];
152   [panel_ setDefaultButtonTitle:l10n_util::GetNSString(IDS_OK)];
153   [panel_ setAlternateButtonTitle:l10n_util::GetNSString(IDS_CANCEL)];
154   SecPolicyRef sslPolicy;
155   if (net::x509_util::CreateSSLClientPolicy(&sslPolicy) == noErr) {
156     [panel_ setPolicies:(id)sslPolicy];
157     CFRelease(sslPolicy);
158   }
160   constrainedWindow_.reset(
161       new ConstrainedWindowMac(observer_.get(), webContents, self));
162   observer_->StartObserving();
165 - (void)closeWebContentsModalDialog {
166   DCHECK(constrainedWindow_);
167   constrainedWindow_->CloseWebContentsModalDialog();
170 - (NSWindow*)overlayWindow {
171   return overlayWindow_;
174 - (SFChooseIdentityPanel*)panel {
175   return panel_;
178 - (void)showSheetForWindow:(NSWindow*)window {
179   NSString* title = l10n_util::GetNSString(IDS_CLIENT_CERT_DIALOG_TITLE);
180   overlayWindow_.reset([window retain]);
181   [panel_ beginSheetForWindow:window
182                 modalDelegate:self
183                didEndSelector:@selector(sheetDidEnd:returnCode:context:)
184                   contextInfo:NULL
185                    identities:base::mac::CFToNSCast(identities_)
186                       message:title];
189 - (void)closeSheetWithAnimation:(BOOL)withAnimation {
190   closePending_ = YES;
191   overlayWindow_.reset();
192   // Closing the sheet using -[NSApp endSheet:] doesn't work so use the private
193   // method.
194   [panel_ _dismissWithCode:NSFileHandlingPanelCancelButton];
197 - (void)hideSheet {
198   NSWindow* sheetWindow = [overlayWindow_ attachedSheet];
199   [sheetWindow setAlphaValue:0.0];
201   oldResizesSubviews_ = [[sheetWindow contentView] autoresizesSubviews];
202   [[sheetWindow contentView] setAutoresizesSubviews:NO];
204   oldSheetFrame_ = [sheetWindow frame];
205   NSRect overlayFrame = [overlayWindow_ frame];
206   oldSheetFrame_.origin.x -= NSMinX(overlayFrame);
207   oldSheetFrame_.origin.y -= NSMinY(overlayFrame);
208   [sheetWindow setFrame:ui::kWindowSizeDeterminedLater display:NO];
211 - (void)unhideSheet {
212   NSWindow* sheetWindow = [overlayWindow_ attachedSheet];
213   NSRect overlayFrame = [overlayWindow_ frame];
214   oldSheetFrame_.origin.x += NSMinX(overlayFrame);
215   oldSheetFrame_.origin.y += NSMinY(overlayFrame);
216   [sheetWindow setFrame:oldSheetFrame_ display:NO];
217   [[sheetWindow contentView] setAutoresizesSubviews:oldResizesSubviews_];
218   [[overlayWindow_ attachedSheet] setAlphaValue:1.0];
221 - (void)pulseSheet {
222   // NOOP
225 - (void)makeSheetKeyAndOrderFront {
226   [[overlayWindow_ attachedSheet] makeKeyAndOrderFront:nil];
229 - (void)updateSheetPosition {
230   // NOOP
233 - (void)onConstrainedWindowClosed {
234   observer_->StopObserving();
235   panel_.reset();
236   constrainedWindow_.reset();
237   [self release];
240 @end