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