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