Check USB device path access when prompting users to select a device.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / ssl_client_certificate_selector_cocoa.mm
blob54ff89123d38624b0cf371dea480cbe7c67dc412
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 "chrome/grit/generated_resources.h"
17 #include "components/web_modal/popup_manager.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/client_certificate_delegate.h"
20 #include "content/public/browser/web_contents.h"
21 #include "net/cert/x509_certificate.h"
22 #include "net/cert/x509_util_mac.h"
23 #include "net/ssl/ssl_cert_request_info.h"
24 #include "ui/base/cocoa/window_size_constants.h"
25 #include "ui/base/l10n/l10n_util_mac.h"
27 using content::BrowserThread;
29 @interface SFChooseIdentityPanel (SystemPrivate)
30 // A system-private interface that dismisses a panel whose sheet was started by
31 // -beginSheetForWindow:modalDelegate:didEndSelector:contextInfo:identities:message:
32 // as though the user clicked the button identified by returnCode. Verified
33 // present in 10.5 through 10.8.
34 - (void)_dismissWithCode:(NSInteger)code;
35 @end
37 @interface SSLClientCertificateSelectorCocoa ()
38 - (void)onConstrainedWindowClosed;
39 @end
41 class SSLClientAuthObserverCocoaBridge : public SSLClientAuthObserver,
42                                          public ConstrainedWindowMacDelegate {
43  public:
44   SSLClientAuthObserverCocoaBridge(
45       const content::BrowserContext* browser_context,
46       net::SSLCertRequestInfo* cert_request_info,
47       scoped_ptr<content::ClientCertificateDelegate> delegate,
48       SSLClientCertificateSelectorCocoa* controller)
49       : SSLClientAuthObserver(browser_context,
50                               cert_request_info,
51                               delegate.Pass()),
52         controller_(controller) {}
54   // SSLClientAuthObserver implementation:
55   void OnCertSelectedByNotification() override {
56     [controller_ closeWebContentsModalDialog];
57   }
59   // ConstrainedWindowMacDelegate implementation:
60   void OnConstrainedWindowClosed(ConstrainedWindowMac* window) override {
61     // |onConstrainedWindowClosed| will delete the sheet which might be still
62     // in use higher up the call stack. Wait for the next cycle of the event
63     // loop to call this function.
64     [controller_ performSelector:@selector(onConstrainedWindowClosed)
65                       withObject:nil
66                       afterDelay:0];
67   }
69  private:
70   SSLClientCertificateSelectorCocoa* controller_;  // weak
73 namespace chrome {
75 void ShowSSLClientCertificateSelector(
76     content::WebContents* contents,
77     net::SSLCertRequestInfo* cert_request_info,
78     scoped_ptr<content::ClientCertificateDelegate> delegate) {
79   DCHECK_CURRENTLY_ON(BrowserThread::UI);
81   // Not all WebContentses can show modal dialogs.
82   //
83   // TODO(davidben): Move this hook to the WebContentsDelegate and only try to
84   // show a dialog in Browser's implementation. https://crbug.com/456255
85   if (web_modal::PopupManager::FromWebContents(contents) == nullptr)
86     return;
88   // The dialog manages its own lifetime.
89   SSLClientCertificateSelectorCocoa* selector =
90       [[SSLClientCertificateSelectorCocoa alloc]
91           initWithBrowserContext:contents->GetBrowserContext()
92                  certRequestInfo:cert_request_info
93                         delegate:delegate.Pass()];
94   [selector displayForWebContents:contents];
97 }  // namespace chrome
99 @implementation SSLClientCertificateSelectorCocoa
101 - (id)initWithBrowserContext:(const content::BrowserContext*)browserContext
102              certRequestInfo:(net::SSLCertRequestInfo*)certRequestInfo
103                     delegate:(scoped_ptr<content::ClientCertificateDelegate>)
104                                  delegate {
105   DCHECK(browserContext);
106   DCHECK(certRequestInfo);
107   if ((self = [super init])) {
108     observer_.reset(new SSLClientAuthObserverCocoaBridge(
109         browserContext, certRequestInfo, delegate.Pass(), self));
110   }
111   return self;
114 - (void)sheetDidEnd:(NSWindow*)parent
115          returnCode:(NSInteger)returnCode
116             context:(void*)context {
117   net::X509Certificate* cert = NULL;
118   if (returnCode == NSFileHandlingPanelOKButton) {
119     CFRange range = CFRangeMake(0, CFArrayGetCount(identities_));
120     CFIndex index =
121         CFArrayGetFirstIndexOfValue(identities_, range, [panel_ identity]);
122     if (index != -1)
123       cert = certificates_[index].get();
124     else
125       NOTREACHED();
126   }
128   if (!closePending_) {
129     // If |closePending_| is already set, |closeSheetWithAnimation:| was called
130     // already to cancel the selection rather than continue with no
131     // certificate. Otherwise, tell the backend which identity (or none) the
132     // user selected.
133     userResponded_ = YES;
134     observer_->CertificateSelected(cert);
136     constrainedWindow_->CloseWebContentsModalDialog();
137   }
140 - (void)displayForWebContents:(content::WebContents*)webContents {
141   // Create an array of CFIdentityRefs for the certificates:
142   size_t numCerts = observer_->cert_request_info()->client_certs.size();
143   identities_.reset(CFArrayCreateMutable(
144       kCFAllocatorDefault, numCerts, &kCFTypeArrayCallBacks));
145   for (size_t i = 0; i < numCerts; ++i) {
146     SecCertificateRef cert =
147         observer_->cert_request_info()->client_certs[i]->os_cert_handle();
148     SecIdentityRef identity;
149     if (SecIdentityCreateWithCertificate(NULL, cert, &identity) == noErr) {
150       CFArrayAppendValue(identities_, identity);
151       CFRelease(identity);
152       certificates_.push_back(observer_->cert_request_info()->client_certs[i]);
153     }
154   }
156   // Get the message to display:
157   NSString* message = l10n_util::GetNSStringF(
158       IDS_CLIENT_CERT_DIALOG_TEXT,
159       base::ASCIIToUTF16(
160           observer_->cert_request_info()->host_and_port.ToString()));
162   // Create and set up a system choose-identity panel.
163   panel_.reset([[SFChooseIdentityPanel alloc] init]);
164   [panel_ setInformativeText:message];
165   [panel_ setDefaultButtonTitle:l10n_util::GetNSString(IDS_OK)];
166   [panel_ setAlternateButtonTitle:l10n_util::GetNSString(IDS_CANCEL)];
167   SecPolicyRef sslPolicy;
168   if (net::x509_util::CreateSSLClientPolicy(&sslPolicy) == noErr) {
169     [panel_ setPolicies:(id)sslPolicy];
170     CFRelease(sslPolicy);
171   }
173   constrainedWindow_.reset(
174       new ConstrainedWindowMac(observer_.get(), webContents, self));
175   observer_->StartObserving();
178 - (void)closeWebContentsModalDialog {
179   DCHECK(constrainedWindow_);
180   constrainedWindow_->CloseWebContentsModalDialog();
183 - (NSWindow*)overlayWindow {
184   return overlayWindow_;
187 - (SFChooseIdentityPanel*)panel {
188   return panel_;
191 - (void)showSheetForWindow:(NSWindow*)window {
192   NSString* title = l10n_util::GetNSString(IDS_CLIENT_CERT_DIALOG_TITLE);
193   overlayWindow_.reset([window retain]);
194   [panel_ beginSheetForWindow:window
195                 modalDelegate:self
196                didEndSelector:@selector(sheetDidEnd:returnCode:context:)
197                   contextInfo:NULL
198                    identities:base::mac::CFToNSCast(identities_)
199                       message:title];
202 - (void)closeSheetWithAnimation:(BOOL)withAnimation {
203   if (!userResponded_) {
204     // If the sheet is closed by closing the tab rather than the user explicitly
205     // hitting Cancel, |closeSheetWithAnimation:| gets called before
206     // |sheetDidEnd:|. In this case, the selection should be canceled rather
207     // than continue with no certificate. The |returnCode| parameter to
208     // |sheetDidEnd:| is the same in both cases.
209     observer_->CancelCertificateSelection();
210   }
211   closePending_ = YES;
212   overlayWindow_.reset();
213   // Closing the sheet using -[NSApp endSheet:] doesn't work so use the private
214   // method.
215   [panel_ _dismissWithCode:NSFileHandlingPanelCancelButton];
218 - (void)hideSheet {
219   NSWindow* sheetWindow = [overlayWindow_ attachedSheet];
220   [sheetWindow setAlphaValue:0.0];
222   oldResizesSubviews_ = [[sheetWindow contentView] autoresizesSubviews];
223   [[sheetWindow contentView] setAutoresizesSubviews:NO];
225   oldSheetFrame_ = [sheetWindow frame];
226   NSRect overlayFrame = [overlayWindow_ frame];
227   oldSheetFrame_.origin.x -= NSMinX(overlayFrame);
228   oldSheetFrame_.origin.y -= NSMinY(overlayFrame);
229   [sheetWindow setFrame:ui::kWindowSizeDeterminedLater display:NO];
232 - (void)unhideSheet {
233   NSWindow* sheetWindow = [overlayWindow_ attachedSheet];
234   NSRect overlayFrame = [overlayWindow_ frame];
235   oldSheetFrame_.origin.x += NSMinX(overlayFrame);
236   oldSheetFrame_.origin.y += NSMinY(overlayFrame);
237   [sheetWindow setFrame:oldSheetFrame_ display:NO];
238   [[sheetWindow contentView] setAutoresizesSubviews:oldResizesSubviews_];
239   [[overlayWindow_ attachedSheet] setAlphaValue:1.0];
242 - (void)pulseSheet {
243   // NOOP
246 - (void)makeSheetKeyAndOrderFront {
247   [[overlayWindow_ attachedSheet] makeKeyAndOrderFront:nil];
250 - (void)updateSheetPosition {
251   // NOOP
254 - (NSWindow*)sheetWindow {
255   return panel_;
258 - (void)onConstrainedWindowClosed {
259   observer_->StopObserving();
260   panel_.reset();
261   constrainedWindow_.reset();
262   [self release];
265 @end