ssl_client_certificate_selector.cc revision a02191e04bc25c4935f804f2c080ae28663d096d
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#include "chrome/browser/ui/views/ssl_client_certificate_selector.h" 6 7#include "base/compiler_specific.h" 8#include "base/i18n/time_formatting.h" 9#include "base/logging.h" 10#include "base/strings/utf_string_conversions.h" 11#include "chrome/browser/certificate_viewer.h" 12#include "components/web_modal/web_contents_modal_dialog_host.h" 13#include "components/web_modal/web_contents_modal_dialog_manager.h" 14#include "components/web_modal/web_contents_modal_dialog_manager_delegate.h" 15#include "content/public/browser/browser_thread.h" 16#include "content/public/browser/web_contents.h" 17#include "content/public/browser/web_contents_view.h" 18#include "grit/generated_resources.h" 19#include "net/cert/x509_certificate.h" 20#include "net/ssl/ssl_cert_request_info.h" 21#include "ui/base/l10n/l10n_util.h" 22#include "ui/base/models/table_model.h" 23#include "ui/base/models/table_model_observer.h" 24#include "ui/gfx/native_widget_types.h" 25#include "ui/views/controls/button/label_button.h" 26#include "ui/views/controls/label.h" 27#include "ui/views/controls/table/table_view.h" 28#include "ui/views/layout/grid_layout.h" 29#include "ui/views/layout/layout_constants.h" 30#include "ui/views/widget/widget.h" 31#include "ui/views/window/dialog_client_view.h" 32 33#if defined(USE_NSS) 34#include "chrome/browser/ui/crypto_module_password_dialog_nss.h" 35#endif 36 37using content::BrowserThread; 38using content::WebContents; 39using web_modal::WebContentsModalDialogManager; 40using web_modal::WebContentsModalDialogManagerDelegate; 41 42namespace { 43 44// The dimensions of the certificate selector table view, in pixels. 45static const int kTableViewWidth = 400; 46static const int kTableViewHeight = 100; 47 48} // namespace 49 50/////////////////////////////////////////////////////////////////////////////// 51// CertificateSelectorTableModel: 52 53class CertificateSelectorTableModel : public ui::TableModel { 54 public: 55 explicit CertificateSelectorTableModel( 56 net::SSLCertRequestInfo* cert_request_info); 57 58 // ui::TableModel implementation: 59 virtual int RowCount() OVERRIDE; 60 virtual base::string16 GetText(int index, int column_id) OVERRIDE; 61 virtual void SetObserver(ui::TableModelObserver* observer) OVERRIDE; 62 63 private: 64 std::vector<base::string16> items_; 65 66 DISALLOW_COPY_AND_ASSIGN(CertificateSelectorTableModel); 67}; 68 69CertificateSelectorTableModel::CertificateSelectorTableModel( 70 net::SSLCertRequestInfo* cert_request_info) { 71 for (size_t i = 0; i < cert_request_info->client_certs.size(); ++i) { 72 net::X509Certificate* cert = cert_request_info->client_certs[i].get(); 73 base::string16 text = l10n_util::GetStringFUTF16( 74 IDS_CERT_SELECTOR_TABLE_CERT_FORMAT, 75 base::UTF8ToUTF16(cert->subject().GetDisplayName()), 76 base::UTF8ToUTF16(cert->issuer().GetDisplayName())); 77 items_.push_back(text); 78 } 79} 80 81int CertificateSelectorTableModel::RowCount() { 82 return items_.size(); 83} 84 85base::string16 CertificateSelectorTableModel::GetText(int index, 86 int column_id) { 87 DCHECK_EQ(column_id, 0); 88 DCHECK_GE(index, 0); 89 DCHECK_LT(index, static_cast<int>(items_.size())); 90 91 return items_[index]; 92} 93 94void CertificateSelectorTableModel::SetObserver( 95 ui::TableModelObserver* observer) { 96} 97 98/////////////////////////////////////////////////////////////////////////////// 99// SSLClientCertificateSelector: 100 101SSLClientCertificateSelector::SSLClientCertificateSelector( 102 WebContents* web_contents, 103 const net::HttpNetworkSession* network_session, 104 net::SSLCertRequestInfo* cert_request_info, 105 const chrome::SelectCertificateCallback& callback) 106 : SSLClientAuthObserver(network_session, cert_request_info, callback), 107 model_(new CertificateSelectorTableModel(cert_request_info)), 108 web_contents_(web_contents), 109 window_(NULL), 110 table_(NULL), 111 view_cert_button_(NULL) { 112 DVLOG(1) << __FUNCTION__; 113} 114 115SSLClientCertificateSelector::~SSLClientCertificateSelector() { 116 table_->SetModel(NULL); 117} 118 119void SSLClientCertificateSelector::Init() { 120 views::GridLayout* layout = views::GridLayout::CreatePanel(this); 121 SetLayoutManager(layout); 122 123 const int column_set_id = 0; 124 views::ColumnSet* column_set = layout->AddColumnSet(column_set_id); 125 column_set->AddColumn( 126 views::GridLayout::FILL, views::GridLayout::FILL, 127 1, views::GridLayout::USE_PREF, 0, 0); 128 129 layout->StartRow(0, column_set_id); 130 base::string16 text = l10n_util::GetStringFUTF16( 131 IDS_CLIENT_CERT_DIALOG_TEXT, 132 base::ASCIIToUTF16(cert_request_info()->host_and_port.ToString())); 133 views::Label* label = new views::Label(text); 134 label->SetMultiLine(true); 135 label->SetHorizontalAlignment(gfx::ALIGN_LEFT); 136 label->SetAllowCharacterBreak(true); 137 layout->AddView(label); 138 139 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); 140 141 CreateCertTable(); 142 layout->StartRow(1, column_set_id); 143 layout->AddView(table_->CreateParentIfNecessary(), 1, 1, 144 views::GridLayout::FILL, 145 views::GridLayout::FILL, kTableViewWidth, kTableViewHeight); 146 147 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); 148 149 StartObserving(); 150 151 WebContentsModalDialogManager* web_contents_modal_dialog_manager = 152 WebContentsModalDialogManager::FromWebContents(web_contents_); 153 WebContentsModalDialogManagerDelegate* modal_delegate = 154 web_contents_modal_dialog_manager->delegate(); 155 DCHECK(modal_delegate); 156 window_ = views::Widget::CreateWindowAsFramelessChild( 157 this, modal_delegate->GetWebContentsModalDialogHost()->GetHostView()); 158 web_contents_modal_dialog_manager->ShowModalDialog( 159 window_->GetNativeView()); 160 161 // Select the first row automatically. This must be done after the dialog has 162 // been created. 163 table_->Select(0); 164} 165 166net::X509Certificate* SSLClientCertificateSelector::GetSelectedCert() const { 167 int selected = table_->FirstSelectedRow(); 168 if (selected >= 0 && 169 selected < static_cast<int>( 170 cert_request_info()->client_certs.size())) 171 return cert_request_info()->client_certs[selected].get(); 172 return NULL; 173} 174 175/////////////////////////////////////////////////////////////////////////////// 176// SSLClientAuthObserver implementation: 177 178void SSLClientCertificateSelector::OnCertSelectedByNotification() { 179 DVLOG(1) << __FUNCTION__; 180 DCHECK(window_); 181 window_->Close(); 182} 183 184/////////////////////////////////////////////////////////////////////////////// 185// DialogDelegateView implementation: 186 187bool SSLClientCertificateSelector::CanResize() const { 188 return true; 189} 190 191base::string16 SSLClientCertificateSelector::GetWindowTitle() const { 192 return l10n_util::GetStringUTF16(IDS_CLIENT_CERT_DIALOG_TITLE); 193} 194 195void SSLClientCertificateSelector::DeleteDelegate() { 196 DVLOG(1) << __FUNCTION__; 197 delete this; 198} 199 200bool SSLClientCertificateSelector::IsDialogButtonEnabled( 201 ui::DialogButton button) const { 202 if (button == ui::DIALOG_BUTTON_OK) 203 return !!GetSelectedCert(); 204 return true; 205} 206 207bool SSLClientCertificateSelector::Cancel() { 208 DVLOG(1) << __FUNCTION__; 209 StopObserving(); 210 CertificateSelected(NULL); 211 212 return true; 213} 214 215bool SSLClientCertificateSelector::Accept() { 216 DVLOG(1) << __FUNCTION__; 217 scoped_refptr<net::X509Certificate> cert = GetSelectedCert(); 218 if (cert) { 219 // Remove the observer before we try unlocking, otherwise we might act on a 220 // notification while waiting for the unlock dialog, causing us to delete 221 // ourself before the Unlocked callback gets called. 222 StopObserving(); 223#if defined(USE_NSS) 224 chrome::UnlockCertSlotIfNecessary( 225 cert, 226 chrome::kCryptoModulePasswordClientAuth, 227 cert_request_info()->host_and_port, 228 window_->GetNativeView(), 229 base::Bind(&SSLClientCertificateSelector::Unlocked, 230 base::Unretained(this), 231 cert)); 232#else 233 Unlocked(cert); 234#endif 235 return false; // Unlocked() will close the dialog. 236 } 237 238 return false; 239} 240 241views::View* SSLClientCertificateSelector::GetInitiallyFocusedView() { 242 return table_; 243} 244 245views::View* SSLClientCertificateSelector::CreateExtraView() { 246 DCHECK(!view_cert_button_); 247 view_cert_button_ = new views::LabelButton(this, 248 l10n_util::GetStringUTF16(IDS_PAGEINFO_CERT_INFO_BUTTON)); 249 view_cert_button_->SetStyle(views::Button::STYLE_BUTTON); 250 return view_cert_button_; 251} 252 253ui::ModalType SSLClientCertificateSelector::GetModalType() const { 254#if defined(USE_ASH) 255 return ui::MODAL_TYPE_CHILD; 256#else 257 return views::WidgetDelegate::GetModalType(); 258#endif 259} 260 261/////////////////////////////////////////////////////////////////////////////// 262// views::ButtonListener implementation: 263 264void SSLClientCertificateSelector::ButtonPressed( 265 views::Button* sender, const ui::Event& event) { 266 if (sender == view_cert_button_) { 267 net::X509Certificate* cert = GetSelectedCert(); 268 if (cert) 269 ShowCertificateViewer(web_contents_, 270 web_contents_->GetView()->GetTopLevelNativeWindow(), 271 cert); 272 } 273} 274 275/////////////////////////////////////////////////////////////////////////////// 276// views::TableViewObserver implementation: 277void SSLClientCertificateSelector::OnSelectionChanged() { 278 GetDialogClientView()->ok_button()->SetEnabled(!!GetSelectedCert()); 279} 280 281void SSLClientCertificateSelector::OnDoubleClick() { 282 if (Accept()) 283 window_->Close(); 284} 285 286/////////////////////////////////////////////////////////////////////////////// 287// SSLClientCertificateSelector private methods: 288 289void SSLClientCertificateSelector::CreateCertTable() { 290 std::vector<ui::TableColumn> columns; 291 columns.push_back(ui::TableColumn()); 292 table_ = new views::TableView(model_.get(), 293 columns, 294 views::TEXT_ONLY, 295 true /* single_selection */); 296 table_->SetObserver(this); 297} 298 299void SSLClientCertificateSelector::Unlocked(net::X509Certificate* cert) { 300 DVLOG(1) << __FUNCTION__; 301 CertificateSelected(cert); 302 window_->Close(); 303} 304 305namespace chrome { 306 307void ShowSSLClientCertificateSelector( 308 content::WebContents* contents, 309 const net::HttpNetworkSession* network_session, 310 net::SSLCertRequestInfo* cert_request_info, 311 const chrome::SelectCertificateCallback& callback) { 312 DVLOG(1) << __FUNCTION__ << " " << contents; 313 DCHECK_CURRENTLY_ON(BrowserThread::UI); 314 (new SSLClientCertificateSelector( 315 contents, network_session, cert_request_info, callback))->Init(); 316} 317 318} // namespace chrome 319