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